我更改了文字,因此有些评论可能会引用以前的版本
以下是代码示例。有两个线程: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");
}
}
答案 0 :(得分:2)
当主要对象(覆盖finalize()
的那个)不再可到达(符合垃圾回收条件)时,GC将首先调用finalize()
。我看到你正在跟踪它,所以确保你的日志消息实际上被调用。
然而,仅仅调用interrupt()
是不够的,守护程序线程必须主动检查该标志(使用isInterrupted()
)并正确响应它,尽快关闭。如果有的话,你应该正确处理InterruptedException
。
您的线程是守护程序线程这一事实无关紧要。非守护程序线程阻止JVM退出(当所有非守护程序线程完成其工作时,JVM存在)。在这里你手动中断线程 - 该线程是否是守护进程并不重要。
这只是一个垃圾收集器延迟还是线程真的永远不会结束,因为主要对象没有被删除,因为它被记者引用?
您的"Reporting of..."
消息是否曾显示过?你可以在那里放一个断点。这意味着实现finalize()
的对象是GC的受害者。如果只有reporter
线程持有对主对象的引用(反之亦然),那么如果没有外部引用它们,GC仍将释放这些对象,尽管是循环依赖。
与您的问题无关。如果您使用slf4j,请执行以下操作:
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();
}
}