1. 程式人生 > >Java關閉鉤子的應用 - Shutdown Hook

Java關閉鉤子的應用 - Shutdown Hook

article 這一 reac lis cat initial 之間 ram pub

背景

在開發中,遇到這種情況,多個線程同時工作,突然一個線程遇到了fetal的錯誤,需要立即終止程序,等人工排查解決了問題之後重新啟動。但是這樣會有一個問題,程序終止時,其他線程可能正在進行重要操作,比如發一個message到另一個模塊,並更新數據庫狀態。突然終止,可能會讓這個操作只完成一半,從而導致數據不一致。

解決方案是:參考數據庫Transaction原子性的概念,將這一系列重要操作看作一個整體,要麽全部完成,要麽全部不完成。為方便表述,我們把這一系列重要操作記為操作X。
當程序即將退出時,查看當前是否有操作X在執行中,

  • 如果有,等待其完成然後退出。且期間不再接受新的操作X。如果操作X執行之間過長,終止並回滾所有狀態。
  • 如果沒有,則可以立即退出。

在程序退出的時候,做一些Check,保證已經開始的操作X的原子性,這裏就用到了Runtime.ShutdownHook

什麽是Shutdown Hook

Shutdown hook是一個initialized but unstarted thread。當JVM開始執行shutdown sequence時,會並發運行所有registered Shutdown Hook。這時,在Shutdown Hook這個線程裏定義的操作便會開始執行。

需要註意的是,在Shutdown Hook裏執行的操作應當是不太耗時的。因為在用戶註銷或者操作系統關機導致的JVM shutdown的例子中,系統只會預留有限的時間給未完成的工作,超時之後還是會強制關閉。

什麽時候會調用Shutdown Hook

  • 程序正常停止
    • Reach the end of program
    • System.exit
  • 程序異常退出
    • NPE
    • OutOfMemory
  • 受到外界影響停止
    • Ctrl+C
    • 用戶註銷或者關機

如何使用Shutdown Hook

調用java.lang.Runtime這個類的addShutdownHook(Thread hook)方法即可註冊一個Shutdown Hook,然後在Thread中定義需要在system exit時進行的操作。如下:

Runtime.getRuntime().addShutdownHook(new Thread(() -> System.out
.println("Do something in Shutdown Hook")));

測試例子

首先,註冊了一個Shutdown Hook
然後,系統Sleep 3秒,模擬進行某些操作。
然後,調用一個空的List,拋出異常,準備結束程序。
在程序將要結束的時候,執行Shutdown Hook中的內容。

public static void main(String[] args)
{
    // register shutdown hook
    Runtime.getRuntime().addShutdownHook(new Thread(() -> System.out.println("Do something in Shutdown Hook")));

    // sleep for some time
    try {
        for (int i=0; i<3; i++) {
            System.out.println("Count: " + i + "...");
            TimeUnit.MILLISECONDS.sleep(1000);
        }
        List nullList = new ArrayList<>();
        System.out.println("Trying to print null list‘s first element: " + nullList.get(0).toString());
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    System.out.println("Ready to exit.");
    System.exit(0);
}

結果如下:

Count: 0...
Count: 1...
Count: 2...
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
    at java.util.ArrayList.rangeCheck(ArrayList.java:653)
    at java.util.ArrayList.get(ArrayList.java:429)
    at HookTest.main(HookTest.java:18)
Do something in Shutdown Hook

Process finished with exit code 1

需要註意的點

  • System.exit之後,當Shutdown Hook開始執行時,其他的線程還是會繼續執行。
  • 應當保證Shutdown Hook的線程安全。
  • 在使用多個Shutdown Hook時一定要特別小心,保證其調用的服務不會被其他Hook影響。否則會出現當前Hook所依賴的服務被另外一個Hook終止了的情況。

鏈接

  • Java Doc - Runtime
  • JAVA虛擬機關閉鉤子(Shutdown Hook)

Java關閉鉤子的應用 - Shutdown Hook