Shutdown Hook - JAVA虚拟机关闭钩子

2,179 阅读3分钟

Shutdown Hooks是一种特殊的结构,它允许开发人员插入JVM关闭时执行的一段代码

用途

  1. Application正常退出(所有线程完成时,或在调用 System.exit(0) 时),执行特定的业务逻辑或关闭资源等操作
  2. Application非正常退出(用户按下Ctrl+C、操作系统关闭(kill pid,不带-9)),在退出时执行必要的挽救措施

用法

示例:

public class ShutDownHook {
    public static void main(String[] args) {

        Runtime.getRuntime()
               .addShutdownHook(new Thread(() -> System.out.println("Shutdown Hook is running !")));
        System.out.println("Application Terminating ...");
    }
}

输出:

Application Terminating ...
Shutdown Hook is running !

可以看到 Shutdown Hook is running ! 输出在 Application Terminating ... 之后

陷阱

1.在某些情况下,可能无法执行Shutdown Hook

如果JVM由于某些内部错误而崩溃,则它可能崩溃而没有机会执行一条指令。另外,如果操作系统发出SIGKILL信号(在Unix/Linux中为kill -9)或TerminateProcess(Windows),则要求应用程序立即终止而无需甚至在等待任何清理活动。除上述内容外,还可以通过调用 Runtime.halt() 方法来终止JVM,而不允许运行Shutdown Hook。

2.启动后,可以在完成之前强行关闭Shutdown Hook

在诸如操作系统关闭之类的情况下,有可能在Shutdown Hook完成之前将其终止。在这种情况下,一旦给出SIGTERM,O/S将等待进程终止指定的时间。如果该过程未在此期限内终止,则操作系统将通过发出SIGTERM(或Windows中的对应程序)来强制终止该过程。因此,当关闭Shutdown Hook执行到一半时,可能会发生这种情况。

因此,建议确保Shutdown Hook的谨慎书写,以确保它们快速完成,并且不会引起死锁等情况。另外,JavaDoc特别提到不应在Shutdown Hook中执行长时间计算或等待用户I/O操作。

3.可以有多个Shutdown Hook,但是不能保证它们的执行顺序

可以注册多个Shutdown Hook,但是JVM无法保证其执行顺序(shutdownHooks存放在IdentityHashMap中),JVM可以按任意顺序执行关闭Shutdown Hook,也可能会同时执行Shutdown Hooks。

public void addShutdownHook(Thread hook) {
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        sm.checkPermission(new RuntimePermission("shutdownHooks"));
    }
    ApplicationShutdownHooks.add(hook);
}

class ApplicationShutdownHooks {
    /* The set of registered hooks */
    private static IdentityHashMap<Thread, Thread> hooks;
    static synchronized void add(Thread hook) {
        if(hooks == null)
            throw new IllegalStateException("Shutdown in progress");

        if (hook.isAlive())
            throw new IllegalArgumentException("Hook already running");

        if (hooks.containsKey(hook))
            throw new IllegalArgumentException("Hook previously registered");

        hooks.put(hook, hook);
    }
}

4.关闭程序开始后,无法注册/取消注册Shutdown Hook

一旦关闭程序是由JVM发起的,将不在允许添加或删除任何现有的Shutdown Hook,否则抛出IllegalStateException异常。

5.关闭程序开始后,只能由Runtime.halt()停止

一旦关闭程序开始,只有 Runtime.halt()(强制终止JVM)可以停止执行关闭序列(除了诸如SIGKILL之类的外部影响之外)。这意味着在Shutdown Hook中调用 System.exit() 将不起作用。实际上,如果在Shutdown Hook中调用 System.exit(),VM可能会卡住,我们可能不得不强制终止该过程。

6.使用Shutdown Hook需要安全权限

如果我们使用的是Java Security Manager,则执行添加/删除Shutdown Hook的代码在运行时需要具有shutdownHooks权限。如果我们在安全的环境中未经许可调用此方法,则将导致SecurityException。



参考资料: GeeksforGeeks - JVM Shutdown Hook in Java