Skip to content
Advertisement

Java: is it possible to create a garbage-collectable object that includes an internal heartbeat thread?

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?

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 🙂

Advertisement