JVM 如何终止守护线程?或如何编写优雅终止的守护线程
Posted
技术标签:
【中文标题】JVM 如何终止守护线程?或如何编写优雅终止的守护线程【英文标题】:How does the JVM terminate daemon threads? or How to write daemon threads that terminate gracefully 【发布时间】:2012-01-29 14:10:54 【问题描述】:假设场景: 我有一个守护线程负责一些 I/O,主线程完成并返回,JVM 决定终止我的守护线程。
它是如何做到的?打断?敲定?如何编码我的守护线程,以便它在终止时做出优雅的反应?
【问题讨论】:
你看源码了吗? @StephenC 我没有想到这一点,并且肯定会产生一个明确的答案(尽管不是必然有用的)。然而,我个人并没有足够的勇气去尝试,也不希望其他人这样做。 好吧,让我更明确一点。回答此类问题的最佳方法是查看源代码,或者至少查看 Java 代码。一般来说,它很容易阅读并且评论很好。 (而且我不明白一个问题的明确答案如何不如一个非明确的答案有用......尤其是如果 you 是阅读代码的人!) 告诉 OP 查看源代码是没有答案的。这显示了特定的实现,而不是 API 要求。文档有它的位置。 【参考方案1】:我只是写了以下代码作为测试:
public class DaemonThreadPlay
public static void main(String [] args)
Thread daemonThread = new Thread()
public void run()
while (true)
try
System.out.println("Try block executed");
Thread.sleep(1000l);
catch (Throwable t)
t.printStackTrace();
@Override
public void finalize()
System.out.println("Finalize method called");
;
daemonThread.setDaemon(true);
daemonThread.start();
try
Thread.sleep(2500l);
catch (Throwable t)
//NO-OP
我在守护线程的 catch 块和 finalize 方法中设置了断点。即使执行了 try 块,也没有到达断点。显然这段代码存在同步/计时问题,但我认为我们可以安全地得出结论,守护线程不会在关闭时中断,也不一定会调用它们的 finalize() 方法。
您始终可以向 JVM 运行时添加关闭挂钩:
Thread shutdownHook = ... // construct thread that somehow
// knows about all the daemon threads
Runtime.getRuntime().addShutdownHook(shutdownHook);
您的关闭挂钩显然可以执行“优雅”关闭所需的任何任务。
【讨论】:
我喜欢关机钩子。 在寻找获取活动线程的方法时发现 this。必须遍历数组以检查至少一个用户线程。 我认为关闭钩子是最好的解决方案,以前从不知道这个功能。我想我会在我的 Main 类中保留对我的关闭挂钩的引用,并将我的守护程序线程的集合保存在关闭挂钩中。然后我就可以打断他们了。一旦我实现它,我会尝试发布我的代码。 如果你显式调用System.gc()
并且没有设置JVM选项-XX:+DisableExplicitGC
,那么守护线程将会退出。这意味着垃圾收集器负责在所有用户线程完成后关闭守护线程。
您可以做的另一件事是引入 finally 块,无论在 try-catch 块中发生什么,都可以保证调用它!通过这种方式,您可以关闭 Daemon 线程打开的任何资源并在其中实现任何优雅终止!
@skiller3 小心不要妄下结论。在可视化调试期间停止 JVM 时,很可能根本不会命中断点,因为 JVM 已经与调试器断开连接(我用 IntelliJ IDEA 见证了这种行为)。【参考方案2】:
我认为你误解了守护线程是什么。
见what is a daemon thread in java
总而言之,它基本上意味着守护线程不应该进行任何 I/O 或持有任何资源。如果您违反了这个基本规则,那么您的线程不符合成为守护线程的条件。
添加关闭挂钩是确保您的代码在 JVM 终止之前被调用的标准方法,但即使这样也不能 100% 保证 - 例如,您的 JVM 可能会崩溃,让操作系统以一种方式整理资源保护操作系统,但很可能使您的应用程序处于不一致/错误状态。
系统检查点和恢复机制可以追溯到软件的早期(例如操作系统和批处理操作),不幸的是,这个***不断被重新发明,因为没有解决这个问题的“银弹”方法 (API)以足够通用的方式解决问题。
【讨论】:
Daemon 线程不应该做 IO 或持有资源的说法的依据是什么? 点击提供的链接,您将获得一些见解。 对不起,我不同意。只要知道守护线程可以在处理过程中被杀死,就没有危险,除非你知道一些我不知道的关于 java 资源泄漏的事情。 如果守护线程拥有数百个资源,那么终止 JVM 是很疯狂的,更糟糕的是,当它处于一系列关键写入操作的中间时会发生关闭。您实际上是在冒着应用程序一致性问题的风险。您也不同意撰写“实践中的 Java 并发”的 Brian Goetz……不同意也没关系,但我认为在极少数情况下您可能是正确的。【参考方案3】:AFAIK,守护线程并不是真正用于主流 I/O 工作。如果所有线程都完成,JVM 可能会突然关闭所有守护线程。根据您的要求,可能的解决方法是创建一个 ExecutorService,如下所示:
ExecutorService execPool = Executors.newSingleThreadExecutor(new ThreadFactory()
@Override
public Thread newThread(Runnable runnable)
Thread thread = Executors.defaultThreadFactory().newThread(runnable);
thread.setDaemon(true);
return thread;
);
从 Shutdown hook 调用 executorservice 关闭方法。
Runtime.getRuntime().addShutdownHook(....)
【讨论】:
您的 newThread 方法确实每次都会创建一个新的线程工厂。对Executors.defaultThreadFactory()
的调用只返回一个new DefaultThreadFactory()
【参考方案4】:
使用中断和加入:
class Daemon extends Thread
public void run()
while (true)
try
Thread.sleep(1000);
catch (InterruptedException e)
System.out.println(e);
break;
System.out.println("exit run()");
public class So8663107
public static void main(String[] arguments) throws InterruptedException
System.out.println(Thread.activeCount());
Daemon daemon = new Daemon();
daemon.setDaemon(true);
daemon.start();
Thread.sleep(2500);
System.out.println(Thread.activeCount());
daemon.interrupt();
daemon.join();
System.out.println(Thread.activeCount());
【讨论】:
我认为您没有理解这个问题。我不想中断我的守护线程。我想知道JVM如何终止它们,以及如何处理它。 抱歉,我试图回答:“我怎样才能对我的守护线程进行编码以便它优雅地终止?”以上是关于JVM 如何终止守护线程?或如何编写优雅终止的守护线程的主要内容,如果未能解决你的问题,请参考以下文章