通常,Java 进程可以在所有非守护线程终止后停止。这通常是 VM 将调用关闭挂钩的时间。
如果您终止进程或调用,挂钩将被提前调用(即仍然存在一些非守护线程)System.exit()
如果我理解正确,您会在所有其他非守护线程完成后尝试做某事(即等待所有其他关闭线程完成)。
这并不容易实现。您可以尝试的是访问根ThreadGroup
(它没有父级,使用Thread.currentThread().getGroup()
然后向上移动)。所有线程都是该组(或其子组)的子组。
所以你可以遍历这棵树,直到只剩下守护线程:
int maxWait = 100;
while(countNonDaemonThreads() > 0 && --maxWait > 0) {
Thread.sleep(100);
}
这应该可以,但我还没有测试过。请注意,您需要小心关闭线程中的代码;它不能阻塞,您必须以某种方式处理所有错误,否则 VM 可能无法正确终止。
编辑The code in 中的代码java.lang.Shutdown
是单线程的,因此如果其中一个关闭挂钩阻塞,则整个 VM 挂起。
现在回答你的问题:
halt()
VM 在被调用时终止。这是最后一件事Terminate.run()
,所以是的,VM应该在run()
. 我什至认为run()
永远不会回来。可能有错误和本机代码可能会阻碍,但这就是它应该的样子。
一个好的做法是使用线程池来完成您的工作并等待它们完成。关闭挂钩是最后的手段。由于您无法真正影响顺序,并且您无法确定非守护线程是否仍然存在,因此对于“最后关灯”类型的工作来说,它们是脆弱的。
使用线程池,您可以拥有多个独立于它们的线程池,您可以清楚地定义如何关闭池以及如何等待它们完成。最重要的是,您可以以可以中止它们的方式构建线程。当您处于关闭钩子中并且一个非守护线程拒绝死亡时,您要做什么?使用线程池,您将知道可能在其中的线程类型以及简单终止它的风险。