shutdownHook
是一種特殊的結構,它容許開發人員插入JVM關閉時執行
的一段代碼。這種狀況在咱們須要作特殊清理操做
的狀況下頗有用html
<!-- more -->java
在Jboss
,Jetty
等容器中均可以看到shutdownHook
的身影,例如在服務優雅下線一文中的spring-boot-starter-actuator
就會觸發shutdownHook
...git
Application
正常退出,在退出時執行特定的業務邏輯,或者關閉資源等操做。正常退出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調用shutdownHook
。api
正在工做... ... 正在工做... 退出程序...
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
。
微信公衆號:battcn
(歡迎調戲)