1

I have changed the text, so some comments may refer previous version

Below is code sample. There are two threads: observer and observable. Observable is started by main. Observer is started by observable object creation and intended to end with it's destruction. But it doesn't happen and observer runs forever. Why?

public class ThreadObjectReaping01 {

private static final Logger log = LoggerFactory.getLogger(ThreadObjectReaping01.class);

public static void main(String[] args) throws InterruptedException {

    Thread observable = new Thread("observable") {

        private Thread observable2 = this;

        Thread observer = new Thread("observer") {
            @Override
            public void run() {

                log.info("Observer is starting");

                while(!interrupted()) {

                    if( observable2.isAlive() ) {
                        log.info("Observable is running");
                    }
                    else {
                        log.info("Observable is NOT running");
                    }

                    try {
                        sleep(1000);
                    } catch (InterruptedException e) {
                        interrupt();
                    }
                }

                log.info("Observer is terminating");

            }
        };

        {
            observer.start();
        }

        @Override
        protected void finalize() throws Throwable {
            observer.interrupt();
        }

        @Override
        public void run() {

            log.info("Observable is starting");

            while(!interrupted()) {
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                    interrupt();
                }
                //log.info("Observable is running");
            }


        }
    };

    log.info("Main is starting observable");
    observable.start();
    Thread.sleep(10000);
    log.info("Main is interrupting observable");
    observable.interrupt();
    observable = null;
    Thread.sleep(10000);
    log.info("Main is terminating");

}
}
Dims
  • 47,675
  • 117
  • 331
  • 600

2 Answers2

2

The moment you main object (the one overriding finalize()) is no longer reachable (eligible for garbage collection), GC will call finalize() first. I see you are tracking it down, so make sure your logging message is in fact called.

However barely calling interrupt() is not enough, the daemon thread must actively check that flag (using isInterrupted()) and correctly respond to it, shutting down as soon as possible. Also you should correctly handle InterruptedException if any.

The fact that your thread is a daemon thread is irrelevant. Non-daemon threads are preventing JVM to exit (JVM exists when all non-daemon threads finished their work). Here you are interrupting the thread manually - it doesn't matter whether that thread is daemon or not.

Is this just a garbage collector delay or the thread will really never end since the main object is not deleted due to it is referenced by reporter?

Is your "Reporting of..." message ever displayed? You can put a breakpoint there. This means the object implementing finalize() was a victim of GC. If only reporter thread holds a reference to your main object (and vice-versa), GC will still release these object if none of them is referenced externally, despite circular dependency.

See also:

Post Scriptum

Not related to your question. If you are using , this:

log.info(String.format("Reporting of (%s) is about ot interrupt", getName()));

can be replaced by:

log.info("Reporting of {} is about to interrupt", getName());
Community
  • 1
  • 1
Tomasz Nurkiewicz
  • 334,321
  • 69
  • 703
  • 674
  • No, my reporting message does not displayed which means finalize() does not called. This is the question! I have no references to main thread object except hidden reference from reporter object since it is inner class. – Dims Aug 06 '12 at 13:51
2

I noticed, that reporter threads do not end despite it is coded in main thread

A thread ends when it finishes the run() method either because of an exception or a return. If you are talking about thread.interrupt() then this just sets the interrupt flag and causes some methods (sleep, wait, ...) to throw InterruptedException. In your thread you need to test for the interrupt flag:

while (!Thread.currentThread().isInterrupted()) {
     // here's how you should be catching InterruptedException
     try {
        ...
     } catch (InterruptedException e) {
        // re-enable the interrupted flag
        Thread.currentThread.interrupt();
        // probably you should quit the thread too
        return;
     }
}

The thread will be reaped by the garbage collector once it has finished and no one has a reference to the Thread object itself. If main still has a Thread thread field then it will never be garbage collected.

Btw, use of finalize is really discouraged -- especially if you are logging and doing other things there. You should have your own destroy() method or some such that does that sort of cleanup when the thread finishes. Maybe something like:

 public void run() {
     try {
        // do the thread stuff
     } finally {
        destroy();
     }
 }
Gray
  • 115,027
  • 24
  • 293
  • 354
  • Are you sure that it should be no references to thread object for it to be GC? For other objects this is not true: if objects have mutual references they are still garbage collected. – Dims Aug 06 '12 at 13:48
  • P.S. I can't have my own `destroy()` since I have no place to call it. I have main thread objects which I can call from time to time and then just forget about them. At the moment they should garbage collected and associated reporters should be stopped. – Dims Aug 06 '12 at 13:54
  • You might consider _not_ forgetting about them. Maybe maintain a collection of threads when they are forked so you can clean them up. – Gray Aug 06 '12 at 18:25