一塊兒學併發編程 - 鉤子函數(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(歡迎調戲)

相關文章
相關標籤/搜索