一块儿学并发编程 - 钩子函数(shutdownHook)

shutdownHook是一种特殊的结构,它容许开发人员插入JVM关闭时执行的一段代码。这种状况在咱们须要作特殊清理操做的状况下颇有用html

<!-- more -->java

用途

JbossJetty等容器中均可以看到shutdownHook的身影,例如在服务优雅下线一文中的spring-boot-starter-actuator就会触发shutdownHook...git

  • Application正常退出,在退出时执行特定的业务逻辑,或者关闭资源等操做。
  • 虚拟机非正常退出,好比用户按下ctrl+c、OOM宕机、操做系统关闭(kill pid)等。在退出时执行必要的挽救措施。

用法

正常退出spring

public class ShutdownHook {

    public static void main(String[] args) throws InterruptedException {
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            try (FileWriter fw = new FileWriter("hook.log")) {
                // 假设记录日志/或者发送消息
                fw.write("完成销毁操做,回收内存! " + (new Date()).toString());
                System.out.println("退出程序...");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }));
        IntStream.range(0, 10).forEach(i -> {
            try {
                System.out.println("正在工做...");
                Thread.sleep(2_000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
    }
}

当咱们运行上面的代码时,会看到在完成main方法的执行时JVM调用shutdownHookapi

正在工做...
...
正在工做...
退出程序...

日志

kill pid方式安全

关闭进程

命令

注意事项

虽然编写一个shutdownHook很简单,可是须要了解shutdownHook后面的内部部件才能正确使用。所以,在后续中,将探讨shutdownHook设计背后的一些陷阱微信

应用程序没法保证shutdownHook老是运行的oracle

如JVM因为某些内部错误而崩溃,或(Unix / Linux中的kill -9)或TerminateProcess(Windows)),那么应用程序须要当即终止而不会甚至等待任何清理活动。除了上述以外,还能够经过调用Runime.halt()方法来终止JVM,而阻止shutdownHook运行。spring-boot

shutdownHook能够在完成前强行中止spa

虽然shutdownHook开始执行,可是在操做系统关闭的状况下,任然能够在完成以前终止它。在这种状况下,一旦SIGTERM被提供,O/S等待一个进程终止指定的时间。若是进程在该时间限制内没有终止,则O/S经过发出SIGTERM(或Windows中的对等方)强制终止进程。因此有可能这是在shutdownHook中途执行时发生的。

所以,建议谨慎地编写shutdownHook,确保它们快速完成,而且不会形成死锁等状况。另外特别注意的是,不该该执行长时间计算或等待用户I/O操做在钩子。

能够有多个shutdownHook,但其执行顺序没法保证

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

经过源码发现,能够注册多个shutdownHook。可是由于它是存储在IdentityHashMap中的,JVM并不能保证其执行顺序。可是能够同时执行全部的shutdownHook

关闭顺序开始后,没法注册/取消注册shutdownHook

一旦关闭顺序是由JVM发起的,将不在容许添加或删除任何现有的shutdownHook,不然抛出IllegalStateException异常。

关闭顺序开始后,只能由Runtime.halt()中止

关闭顺序开始后,只能经过Runtime.halt()(强制终止JVM),能够中止关闭顺序的执行(外部影响除外,如SIGKILL)。

使用shutdownHook须要安全权限

若是咱们使用Java Security Managers,则执行添加/删除shutdownHook的代码须要在运行时具备shutdownHooks权限。不然会致使SecurityException

参考:http://docs.oracle.com/javase/1.5.0/docs/api/java/lang/Runtime.html#addShutdownHook(java.lang.Thread) "官方文档")

- 说点什么

全文代码:https://gitee.com/battcn/battcn-concurent/tree/master/Chapter1-1/battcn-thread/src/main/java/com/battcn/chapter10

  • 我的QQ:1837307557
  • battcn开源群(适合新手):391619659

微信公众号:battcn(欢迎调戏)

相关文章
相关标签/搜索