Java 如何处理一对相关的线程对象?

Java 如何处理一对相关的线程对象?,java,multithreading,garbage-collection,slf4j,Java,Multithreading,Garbage Collection,Slf4j,我已经更改了文本,因此一些评论可能会引用以前的版本 下面是代码示例。有两条线索:观察者和可观察者。可观察的由main启动。观察者从可观察对象的创建开始,并打算以其破坏结束。但这不会发生,观察家永远在运行。为什么? public class ThreadObjectReaping01 { private static final Logger log = LoggerFactory.getLogger(ThreadObjectReaping01.class); public static vo

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

下面是代码示例。有两条线索:观察者和可观察者。可观察的由main启动。观察者从可观察对象的创建开始,并打算以其破坏结束。但这不会发生,观察家永远在运行。为什么?

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

}
}

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

然而仅仅调用
interrupt()
是不够的,守护进程线程必须主动检查该标志(使用
isInterrupted()
)并正确响应,尽快关闭。此外,您还应正确处理中断异常(如果有)

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

这仅仅是一个垃圾收集器延迟,还是线程永远不会结束,因为主对象没有被删除,因为它被reporter引用

您的
“报告…”
消息是否显示过?您可以在那里设置断点。这意味着实现
finalize()
的对象是GC的受害者。如果只有
reporter
thread持有对主对象的引用(反之亦然),尽管存在循环依赖关系,但如果没有外部引用这些对象,GC仍将释放这些对象

另见:
后脚本 与你的问题无关。如果您正在使用,请执行以下操作:

可替换为:

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

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

然而仅仅调用
interrupt()
是不够的,守护进程线程必须主动检查该标志(使用
isInterrupted()
)并正确响应,尽快关闭。此外,您还应正确处理中断异常(如果有)

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

这仅仅是一个垃圾收集器延迟,还是线程永远不会结束,因为主对象没有被删除,因为它被reporter引用

您的
“报告…”
消息是否显示过?您可以在那里设置断点。这意味着实现
finalize()
的对象是GC的受害者。如果只有
reporter
thread持有对主对象的引用(反之亦然),尽管存在循环依赖关系,但如果没有外部引用这些对象,GC仍将释放这些对象

另见:
后脚本 与你的问题无关。如果您正在使用,请执行以下操作:

可替换为:

log.info("Reporting of {} is about to interrupt", getName());
我注意到,报告线程并没有结束,尽管它是在主线程中编码的

由于异常或返回,线程在完成
run()
方法时结束。如果您谈论的是
thread.interrupt()
,那么这只是设置中断标志,并导致一些方法(sleep、wait等)抛出
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;
     }
}
一旦线程完成并且没有人对
线程
对象本身有引用,垃圾回收器就会获取该线程。如果main仍然有一个
线程
字段,那么它将永远不会被垃圾收集

顺便说一句,不鼓励使用
finalize
,尤其是当您在那里登录和做其他事情时。您应该有自己的
destroy()
方法,或者在线程结束时执行类似清理的方法。可能是这样的:

 public void run() {
     try {
        // do the thread stuff
     } finally {
        destroy();
     }
 }
我注意到,报告线程并没有结束,尽管它是在主线程中编码的

由于异常或返回,线程在完成
run()
方法时结束。如果您谈论的是
thread.interrupt()
,那么这只是设置中断标志,并导致一些方法(sleep、wait等)抛出
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;
     }
}
一旦线程完成并且没有人对
线程
对象本身有引用,垃圾回收器就会获取该线程。如果main仍然有一个
线程
字段,那么它将永远不会被垃圾收集

顺便说一句,不鼓励使用
finalize
,尤其是当您在那里登录和做其他事情时。您应该有自己的
destroy()
方法,或者在线程结束时执行类似清理的方法。可能是这样的:

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

您确定它不应该是线程对象的引用,以便它成为GC吗?对于其他对象,这是不正确的:如果对象具有相互引用,它们仍然是垃圾收集的。我不能拥有自己的
destroy()
,因为我没有地方叫它。我有主线程对象,我可以随时调用它们,然后就把它们忘了。目前,他们应该垃圾收集和相关记者应该停止。你可能会考虑不要忘记他们。可能在分叉时保留一组线程,以便您可以清理它们。您确定它是正确的吗