如何在Java中正常处理SIGKILL信号

当程序收到终止信号时,如何处理清理?

例如,我连接了一个应用程序,希望任何第三方应用程序(我的应用程序)在注销时发送finish命令。 什么是最好的发言,当我的应用程序被kill -9finish命令?

编辑1:kill -9不能被捕获。 谢谢你们纠正我

编辑2:我想这种情况下,当一个调用只是杀死,这是一样的CTRL-C

除了kill -9之外,处理这个事情的方法是注册closures钩子。 如果你可以使用( SIGTERMkill -15closures挂钩将工作。 ( SIGINTkill -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)方法调用,您也可以注册一个信号处理程序,但可能不会像INTTERM那样用于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杀死一个进程,理论上应该知道他/她在做什么,这可能会使事情处于不一致的状态。