如何在Java中正常处理SIGKILL信号
当程序收到终止信号时,如何处理清理?
例如,我连接了一个应用程序,希望任何第三方应用程序(我的应用程序)在注销时发送finish
命令。 什么是最好的发言,当我的应用程序被kill -9
了finish
命令?
编辑1:kill -9不能被捕获。 谢谢你们纠正我
编辑2:我想这种情况下,当一个调用只是杀死,这是一样的CTRL-C
除了kill -9
之外,处理这个事情的方法是注册closures钩子。 如果你可以使用( SIGTERM ) kill -15
closures挂钩将工作。 ( SIGINT ) kill -2
DOES会使程序正常退出并运行closures挂钩。
注册一个新的虚拟机closures挂钩。
Java虚拟机响应两种事件closures:
* The program exits normally, when the last non-daemon thread exits or
当出口(等效地,System.exit)方法被调用,或
* The virtual machine is terminated in response to a user
中断,如键入^ C,或系统范围的事件,如用户注销或系统closures。
我在OSX 10.6.3上试了下面的testing程序,并在kill -9
没有运行shutdown hook,没想到会这样。 在kill -15
它每次都运行closures钩子。
public class TestShutdownHook { public static void main(final String[] args) throws InterruptedException { Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { System.out.println("Shutdown hook ran!"); } }); while (true) { Thread.sleep(1000); } } }
在任何程序中没有任何方法可以很好地处理kill -9
。
在极less数情况下,虚拟机可能会中止,即停止运行而不干净地closures。 当虚拟机在外部终止时会发生这种情况,例如Unix上的SIGKILL信号或Microsoft Windows上的TerminateProcess调用。
处理kill -9
的唯一真正的select是让另一个监视器程序监视主程序消失或使用包装器脚本。 你可以用一个shell脚本来处理这个脚本,这个脚本在列表中寻找你的程序,并在消失时作相应的处理。
#!/bin/bash java TestShutdownHook wait # notify your other app that you quit echo "TestShutdownHook quit"
有些方法可以在某些JVM中处理自己的信号 – 例如,请参阅关于HotSpot JVM的这篇文章 。
通过使用Sun内部的sun.misc.Signal.handle(Signal, SignalHandler)
方法调用,您也可以注册一个信号处理程序,但可能不会像INT
或TERM
那样用于JVM使用的信号。
为了能够处理任何信号,您将不得不跳出JVM并进入操作系统领域。
我通常做的(例如)检测到exception终止是在Perl脚本中启动我的JVM,但让脚本使用waitpid
系统调用等待JVM。
然后,当JVM退出时,我会通知您,为什么退出,并且可以采取必要的措施。
你可以使用Runtime.getRuntime().addShutdownHook(...)
,但不能保证它会在任何情况下被调用。
我期望JVM优雅地中断 ( thread.interrupt()
)应用程序创build的所有正在运行的线程,至less对于信号SIGINT (kill -2)
和SIGTERM (kill -15)
。
这样,信号将被转发给它们,允许以标准的方式优雅的线程取消和资源定位。
但事实并非如此 (至less在我的JVM实现中: Java(TM) SE Runtime Environment (build 1.8.0_25-b17), Java HotSpot(TM) 64-Bit Server VM (build 25.25-b02, mixed mode)
。
正如其他用户所说, closures钩子的使用似乎是强制性的。
那么,我该如何处理呢?
首先,我并不关心所有程序,只在那些我想跟踪用户取消和意外结束的程序中。 例如,假设你的java程序是由其他程序pipe理的。 您可能想区分它是否已正常终止(来自pipe理进程的SIGTERM
)或发生了closures(以便在启动时自动重新启动作业)。
作为基础,我总是让长时间运行的线程周期性地意识到中断状态,如果InterruptedException
则抛出InterruptedException
。 这使得开发人员可以控制执行终止(也产生与标准阻止操作相同的结果)。 然后,在线程堆栈的顶层捕获InterruptedException
,并执行相应的清理。 这个线程被编码为已知如何响应中断请求。 高凝聚力devise。
所以,在这种情况下,我添加了一个closures钩子,这就是我认为JVM默认应该执行的操作:中断我的应用程序创build的所有仍在运行的非守护线程:
Runtime.getRuntime().addShutdownHook(new Thread() { @Override public void run() { System.out.println("Interrupting threads"); Set<Thread> runningThreads = Thread.getAllStackTraces().keySet(); for (Thread th : runningThreads) { if (th != Thread.currentThread() && !th.isDaemon() && th.getClass().getName().startsWith("org.brutusin")) { System.out.println("Interrupting '" + th.getClass() + "' termination"); th.interrupt(); } } for (Thread th : runningThreads) { try { if (th != Thread.currentThread() && !th.isDaemon() && th.isInterrupted()) { System.out.println("Waiting '" + th.getName() + "' termination"); th.join(); } } catch (InterruptedException ex) { System.out.println("Shutdown interrupted"); } } System.out.println("Shutdown finished"); } });
完整的testing应用程序在github上: https : //github.com/idelvall/kill-test
有一种方法可以对kill -9做出反应:即有一个独立的进程监视被杀死的进程,并在必要时进行清理。 这可能会涉及到IPC,并且会有相当多的工作,您仍然可以通过同时杀死这两个进程来覆盖它。 我认为这在大多数情况下是不值得的。
谁用-9杀死一个进程,理论上应该知道他/她在做什么,这可能会使事情处于不一致的状态。