Quick question: Is is possible to create a class such as it contain an internal infinite thread (heartbeat) and its objects are automatically garbage collected?
Long question:
I intend to create a class that includes an internal infinite background thread (such as a heartbeat). Although, the objects of this class shall not need an explicit destroy and shall be garbage-collected when no longer referenced (when, at this point, the heartbeat should also be destroyed), similar C#.
The problem is: Java RE will not garbage-collect these objects because they internally contain a running thread. This leads to an imortal object.
Examples:
Example C# code (works as expected):
using System; using System.Threading; namespace HeartbeatTest { class Program { static void Main(string[] args) { Console.WriteLine("Main execution started."); executeMethod(); // Just wait to see some heartbeats Thread.Sleep(5000); // This shall garbage-collect myObject GC.Collect(); Console.WriteLine("Main execution finished."); } private static void executeMethod() { Console.WriteLine("executeMethod() execution started."); MyObjectWithHeartbeat myObject = new MyObjectWithHeartbeat(); Console.WriteLine("executeMethod() execution finished."); } } class MyObjectWithHeartbeat { private Thread heartbeatThread; public MyObjectWithHeartbeat() { heartbeatThread = new Thread(() => { try { while (true) { Console.WriteLine("heartbeat..."); Thread.Sleep(1000); } } catch (ThreadInterruptedException) { // finish heartbeat execution } }); heartbeatThread.Start(); } ~MyObjectWithHeartbeat() { Console.WriteLine("MyObjectWithHeartbeat destroy"); heartbeatThread.Interrupt(); } } }
C# output:
Main execution started. executeMethod() execution started. executeMethod() execution finished. heartbeat... heartbeat... heartbeat... heartbeat... heartbeat... MyObjectWithHeartbeat destroy Main execution finished.
Example Java code (does not work):
package heartbeattest; public class Main { public static void main(String[] args) { System.out.println("Main execution started."); executeMethod(); // Just wait to see some heartbeats try { Thread.sleep(5000); } catch (InterruptedException e) { e.printStackTrace(); } // This should garbage-collect myObject System.gc(); System.out.println("Main execution finished."); } private static void executeMethod() { System.out.println("executeMethod() execution started."); MyObjectWithHeartbeat myObject = new MyObjectWithHeartbeat(); System.out.println("executeMethod() execution finished."); } } class MyObjectWithHeartbeat { private Thread heartbeatThread; public MyObjectWithHeartbeat() { heartbeatThread = new Thread(new Runnable() { @Override public void run() { try { while (true) { System.out.println("heartbeat..."); Thread.sleep(1000); } } catch (InterruptedException e) { e.printStackTrace(); } } }); heartbeatThread.start(); } @Override protected void finalize() throws Throwable { System.out.println("MyObjectWithHeartbeat destroy"); heartbeatThread.interrupt(); super.finalize(); } }
Java output:
Main execution started. executeMethod() execution started. executeMethod() execution finished. heartbeat... heartbeat... heartbeat... heartbeat... heartbeat... heartbeat... Main execution finished. heartbeat... heartbeat... heartbeat... heartbeat... heartbeat... heartbeat... ... (it keeps executing)
Is there an architectural pattern to overcome this JRE “thread keeps running because object exists – object not destroyed because thread is running” deadlock?
Advertisement
Answer
That’s not how we’d do this in Java. The usual Java appoach would be:
- create and object
- create a thread and pass that object to the thread
- the thread holds a weakReference to the object
- the main loop runs as long as the weak reference is not null
here’s some pseudocode
class Heartbeat extends Thread { WeakReference exists; Heartbeat(Object alive) { exists = new WeakReference(alive) } public void run() { // if the object has not been GC'ed keep running while (exists.get() != null) { // do something } // otherwise terminate the thread } }
String alive = new String("hello!"); // explicit new, believe me! Thread t = new Heartbeat(alive); t.start();
p.s.: please note that System.gc() is NOT granted to garbage collect anything
p.p.s.: we don’t usually write finalizers in the Java world unless we’re writing infrastructure-level libraries 🙂