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 如何终止守护线程?或如何编写优雅终止的守护线程的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 gprof 分析守护进程而不优雅地终止它?

可以杀死守护线程吗? [复制]

37 守护线程是什么?

如何编写 Mono 守护程序

java守护线程

5.1.14 守护线程