在Java中捕获SIGINT信号

48

如何在Java中最好地捕获kill信号而不使用JNI。我发现了sun.misc.Signal和sun.misc.SignalHandler,但警告未来版本可能会删除它们。

那么,我唯一的选择是使用JNI调用C库吗?


相关:在Java中捕获Ctrl+C - ggorlen
3个回答

66
处理这个问题的方法是注册一个shutdown钩子。如果使用(SIGINTkill -2将导致程序优雅地退出并运行关闭钩子。

注册新的虚拟机关机挂钩。

Java虚拟机响应两种类型的事件而关闭:

  • 程序正常退出,当最后一个非守护线程退出或调用exit(等效于System.exit)方法时,或者

  • 虚拟机响应用户中断(例如键入^C)或系统范围事件(例如用户注销或系统关闭)而终止。

我在OSX 10.6.3上尝试了以下测试程序,在kill -9上它没有运行关闭钩子,我认为它不会。在kill -15上,它每次都会运行关闭钩子。

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);
        }
    }
}

这是编写自己的 Java 信号处理程序 的记录方式,而不是关闭钩子。请注意com.sun.misc 包是不受支持的,可能会在任何时候更改或消失,并且很可能只存在于 Sun JVM 中。


13
SIGKILL(9)是一个不可被捕获的信号,这是 Unix 设计的一个特性。 - dolmen
Runtime类是java.lang.Runtime,自JDK 1.0以来就存在。addShutdownHook方法自1.3版本开始存在。因此您可以放心地依赖它们。 - dolmen
1
@dolmen 阅读理解,我在警告关于问题中提到的不应使用的com.sun包。 - user177800
SIGKILL(9)会导致虚拟机中止,根据文档: 如果虚拟机中止,则不能保证是否会运行任何关闭挂钩。 http://docs.oracle.com/javase/7/docs/api/java/lang/Runtime.html#addShutdownHook%28java.lang.Thread%29 - adino
仅供记录:com.sun.*类是可用且维护的,不像sun.*类(请注意com.前缀)。 - Matthieu

7
自Java 6以来,HotSpot的java命令就有一个-Xrs标志,用于禁用JVM信号处理程序。
如果使用它,您需要实现自己的SIGINTSIGTERMSIGHUPSIGQUIT处理程序,包括一个显式调用System.exit()的处理程序。
注意:这意味着当存在-Xrs标志时,SIGQUIT的线程转储功能将被禁用。
请参见https://docs.oracle.com/javase/8/docs/technotes/tools/unix/java.html#BABHDABI(也适用于Windows和Solaris的java对应项)。

当我们使用“-Xrs”启动时,我们应该如何实现这些处理程序? - Aly

4
请看描述HotSpot JVM的能力进行信号链接和异常处理的Integrating Signal and Exception Handling。虽然需要一些C级别的技巧。
您是否考虑过使用java.lang.Runtime.addShutdownHook(Thread)?只要JVM能够执行它们,这些挂钩将在终止时(包括接收到SIGINT)由JVM运行。

1
这是针对整个系统的关闭。虽然它在大多数情况下都有效,但最理想的情况是它能够在继承的进程上工作。 - mjs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接