如何配置一对相关的线程对象?

时间:2012-07-26 17:23:13

标签: java multithreading garbage-collection

我更改了文字,因此有些评论可能会引用以前的版本

以下是代码示例。有两个线程:observer和observable。 Observable由main开始。 Observer由可观察对象创建启动,旨在以其破坏结束。但它不会发生,观察者会永远奔跑。为什么呢?

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");

}
}

2 个答案:

答案 0 :(得分:2)

当主要对象(覆盖finalize()的那个)不再可到达(符合垃圾回收条件)时,GC将首先调用finalize()。我看到你正在跟踪它,所以确保你的日志消息实际上被调用。

然而,仅仅调用interrupt()是不够的,守护程序线程必须主动检查该标志(使用isInterrupted())并正确响应它,尽快关闭。如果有的话,你应该正确处理InterruptedException

您的线程是守护程序线程这一事实无关紧要。非守护程序线程阻止JVM退出(当所有非守护程序线程完成其工作时,JVM存在)。在这里你手动中断线程 - 该线程是否是守护进程并不重要。

  

这只是一个垃圾收集器延迟还是线程真的永远不会结束,因为主要对象没有被删除,因为它被记者引用?

您的"Reporting of..."消息是否曾显示过?你可以在那里放一个断点。这意味着实现finalize()的对象是GC的受害者。如果只有reporter线程持有对主对象的引用(反之亦然),那么如果没有外部引用它们,GC仍将释放这些对象,尽管是循环依赖。

另见:

Post Scriptum

与您的问题无关。如果您使用,请执行以下操作:

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

可以替换为:

log.info("Reporting of {} is about to interrupt", getName());

答案 1 :(得分:2)

  

我注意到,尽管在主线程

中编码,但是记者线程并没有结束

由于异常或返回,线程在完成run()方法时结束。如果你在谈论thread.interrupt(),那么这只是设置中断标志并导致一些方法(睡眠,等待......)抛出InterruptedException。在你的线程中,你需要测试中断标志:

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;
     }
}

一旦完成并且没有人引用Thread对象本身,垃圾收集器将收集该线程。如果main仍然有一个Thread thread字段,那么它永远不会被垃圾收集。

顺便说一下,真的不鼓励使用finalize - 特别是如果你在那里记录和做其他事情。您应该拥有自己的destroy()方法或一些在线程完成时执行此类清理的方法。也许是这样的:

 public void run() {
     try {
        // do the thread stuff
     } finally {
        destroy();
     }
 }