根據 Java API, 所謂 shutdown hook 就是已經初始化但尚未開始執行的線程對象。在Runtime 注冊后,如果JVM要停止前,這些 shutdown hook 便開始執行。也就是在你的程序結束前,
執行一些清理工作,尤其是沒有用戶界面的程序。 這些 shutdown hook 都是些線程對象,因此,你的清理工作要寫在 run() 里。根據 Java API,你的清理工作不能太重了,要盡快結束。
但仍然可以對數據庫進行操作。問題是這個度該如何把握。
Java 虛擬機會為了響應以下兩類事件而關閉:
程序正常退出:這發生在最后的非守護線程退出時,或者在調用 exit(等同於System.exit)方法時。
為響應用戶中斷而終止虛擬機:如鍵入 ^C(注:也就是我們再dos窗口下常用的Ctrl+C退出命令);或發生系統事件,比如用戶注銷或系統關閉。
shutdown hook只是一個已初始化但尚未啟動的線程。虛擬機開始啟用其關閉序列時,它會以某種未指定的順序啟動所有已注冊的關閉掛鈎,並讓它們同時運行。運行完所有的掛鈎后,如果已啟用退出終結,那么虛擬機接着會運行所有未調用的終結方法。最后,虛擬機會暫停。注意,關閉序列期間會繼續運行守護線程,如果通過調用 exit 方法來發起關閉序列,那么也會繼續運行非守護線程。
一旦開始了關閉序列,則只能通過調用 halt 方法來停止這個序列,此方法可強行終止虛擬機。
一旦開始了關閉序列,則不可能注冊新的關閉掛鈎或取消注冊先前已注冊的掛鈎。嘗試執行這些操作會導致拋出 IllegalStateException。
關閉掛鈎可在虛擬機生命周期中的特定時間運行,因此應保護性地對其進行編碼。特別是應將關閉掛鈎編寫為線程安全的,並盡可能地避免死鎖。關閉掛鈎還應該不盲目地依靠某些服務,這些服務可能已注冊了自己的關閉掛鈎,所以其本身可能正處於關閉進程中。
關閉掛鈎應該快速地完成其工作。當程序調用 exit 時,虛擬機應該迅速地關閉並退出。由於用戶注銷或系統關閉而終止虛擬機時,底層的操作系統可能只允許在固定的時間內關閉並退出。因此在關閉掛鈎中嘗試進行任何用戶交互或執行長時間的計算都是不明智的。
與其他所有線程一樣,通過調用線程 ThreadGroup 對象的 uncaughtException 方法,可在關閉掛鈎中處理未捕獲的異常。此方法的默認實現是將該異常的堆棧跟蹤 (stack trace) 打印至 System.err 並終止線程;它不會導致虛擬機退出或暫停。
僅在很少的情況下,虛擬機可能會中止,也就是沒有完全關閉就停止運行。虛擬機被外部終止時會出現這種現象,比如在 Unix 上使用 SIGKILL 信號或者在 Microsoft Windows 上調用 TerminateProcess。如果由於內部數據結構損壞或試圖訪問不存在的內存而導致本機方法執行錯誤,那么可能也會中止虛擬機。如果虛擬機中止,則無法保證是否將運行關閉掛鈎。(比如現在我遇到的 連接數據庫,因為是用的項目初始化內存中的數據連接,用這個方法時在window tomcat上運行正常,到linux中weblogic上運行就異常終止)
注:
在eclipse環境下,程序運行時的那個紅色小方形按鈕是強制終止程序,也就是相當於kill進程。因此這時候,如果你的程序中有一個hook,那么它就不會執行。因為按照上文的說法,只有在兩種情況下運行hook中的程序。這一點和tomcat的程序關閉有點類似,tomcat中的listener中的contextDestroyed()方法也是在jvm關閉的時候才執行這個函數里面的程序。而我們如果直接關掉tomcat的運行窗口,就相當於直接kill掉進程了,因此並不會執行這個函數,但是如果使用的是tomcat里面的shutdown.bat命令,那么這個函數就會執行。
所以對於意外關閉JVM或者強制殺死進程的情況,想要清理垃圾或者一些資源回收的問題,本文的addShutdownHook的方法也不管用。
假如你在程序中還開啟了一個端口,那么意外的程序終止,這個端口還被占用,並沒有被釋放,那么下一次程序重啟或者別的程序要使用這個端口的時候,就會出現端口被占用的情況。
對於addShutdownHook()中的Hook,這個線程里面更不能出現死循環的情況,因為這樣的話,那么當程序正常終止的時候,就會出現程序關閉不了的情況。
原地址:https://blog.csdn.net/wk1134314305/article/details/78504269