我有一个在Ubuntu 16.04 LTS上运行的Java应用程序。
当应用程序收到关闭信号时,关闭序列运行如下:
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
shutdown();
}
});
这样可以正常工作,但是一旦我尝试调用外部REST端点(我使用Rx Observables的Retrofit),线程就会完全消失,端点永远不会被调用,并且连续的命令不再执行。
Call
代替。同样的问题。我认为这与线程有关,例如当一个库在关闭时创建额外的线程时,JVM似乎会杀死所有内容。
请有人光明或建议我还能尝试一下。
-
额外信息:
我需要执行长时间运行的清理工作。问题是我无法确定关闭信号的样子和发生的时间,因为JVM在Docker容器中运行,在docker stop
(当主机停止时),首先发送SIGTERM
,然后,超时后(我可以设置,当前为60秒)SIGKILL
。容器中的JVM运行socket.io,可以连接数千个客户端。我想使用60秒向每个客户端发送good-bye
并干净地断开这些,并从负载均衡器取消注册服务器。因此在清理过程中会有很多潜在的阻塞操作。
如果Java认为清理总是很短,那么Java就错了:(
答案 0 :(得分:2)
在您的情况下关闭JVM时会引发中断的标志。因此,只要您调用任何阻塞操作,就会立即以InterruptedException终止该操作。
如果你必须在应用程序终止时执行更多,可能是持久的代码,那么关闭钩子就不是了。存在的是关闭开放资源,而不是创建新资源。
由于您没有提供有关您的代码的更多信息,我无法给出确切的建议,但一般的想法是让主线程在应用程序运行时等待,并让那个人进行清理工作。在这种情况下,您需要一个不是系统关闭信号的关闭信号,因此应用程序可以继续正常运行。
如果由于某种原因太复杂,您可以尝试通过执行以下操作清除关闭钩子开头的中断标志:
invalidHandler
然而,这违反了规则,而这些存在是有原因的。即操作系统可能会认为你的程序挂起,如果它没有对杀死信号做出足够快的反应并以更糟的方式终止它。