序言:html
每個Java程序均可覺得JVM增長一個關閉鉤子。JVM將在關閉以前執行關閉鉤子中的指令。java
問題:數據庫
一個程序可能須要在退出前執行一些指令。程序可能因爲下列緣由而退出:api
全部的線程已經執行完畢服務器
調用System.exit()併發
用戶輸入Ctrl+Coracle
系統級關閉或用戶註銷ide
適用場景:工具
保存應用狀態,例如,當多數IDE退出時,它們將記憶最後的視圖有哪些。ui
關閉某些數據庫鏈接。
將應用關閉的消息發送給系統管理員。
解決方案:
關閉鉤子支持全部這些場景。應用能夠增長一個關閉鉤子,JVM將在應用退出時調用它。
抽象層次的概念:
將全部的指令(Java代碼)寫入一個線程的run()內,而且調用java.lang.Runtime.addShutdownHook(Thread t)。該方法將這個線程註冊爲JVM的關閉鉤子。在關閉JVM的時候,JVM將並行地運行這些鉤子(線程將在JVM關閉的時候啓動)。
代碼示例:
public class AddShutdownHookSample {
public void attachShutDownHook() {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
System.out.println("Inside Add Shutdown Hook");
}
});
System.out.println("Shut Down Hook Attached.");
}
public static void main(String[] args) {
AddShutdownHookSample sample = new AddShutdownHookSample();
sample.attachShutDownHook();
System.out.println("Last instruction of Program....");
System.exit(0);
}
}
輸出:
Shut Down Hook Attached.
Last instruction of Program....
Inside Add Shutdown Hook
如今明白瞭如何使用addShutDownHook。若是須要能夠增長多個關閉鉤子,可是須要注意的是,鉤子是並行地運行,因此注意併發避免死鎖發生。
在應用中實現關閉鉤子:
關閉鉤子的數量:關閉鉤子的數量沒有限制,若是須要能夠增長多個關閉鉤子。看看run()的修改版本:
public void attachShutDownHook() {
for (int i = 0; i < 10; i++) {
Runtime.getRuntime().addShutdownHook(new Thread() {
@Override
public void run() {
System.out.println("Inside Add Shutdown Hook : "
+ Thread.currentThread().getName());
}
});
}
}
如上,咱們增長了10個關閉鉤子。
什麼時候增長關閉鉤子:任什麼時候候!!!在任何狀況下均可以增長一個關閉鉤子,只要在JVM關閉以前。若是試圖在JVM開始關閉後註冊一個關閉鉤子,將拋出一個帶有」Shutdown is progress」消息的IllegalStateException。
增長相同的鉤子:不能增長相同的鉤子。若是這樣作了,將拋出帶有」Hook previously registered」消息的IllegalArgumentException。
註銷鉤子:調用Runtime.removeShutdownShook(Thread hook)能夠註銷一個鉤子。
注意:大多數時候,使用匿名內部類來註冊關閉鉤子,既然咱們沒有持有這些類的可用引用,咱們也沒法使用要註銷鉤子的匿名內部類,由於咱們須要將它們傳遞給removeShutdownHook(Thread hook)。
注意併發:萬一不止一個關閉鉤子,它們將並行地運行,並容易引起線程問題,例如死鎖。Java Doc對該方法是這樣描述的: /**
* A <i>shutdown hook</i> is simply an initialized but unstarted thread.
* When the virtual machine begins its shutdown sequence it will start all
* registered shutdown hooks in some unspecified order and let them run
* concurrently. When all the hooks have finished it will then run all
* uninvoked finalizers if finalization-on-exit has been enabled. Finally,
* the virtual machine will halt. Note that daemon threads will continue to
* run during the shutdown sequence, as will non-daemon threads if shutdown
* was initiated by invoking the <tt>{@link #exit exit}</tt> method.
*/
翻譯過來:
關閉鉤子只是一個已初始化但還沒有啓動的線程。虛擬機開始啓用其關閉序列時,它會以某種未指定的順序啓動全部已註冊的關閉鉤子,並讓它們同時運行。運行完全部的鉤子後,若是已啓用finalization-on-exit,那麼虛擬機接着會運行全部未調用的finalizers。最後,虛擬機會暫停。注意,關閉序列期間會繼續運行守護線程,若是經過調用exit方法來發起關閉序列,那麼也會繼續運行非守護線程。
關閉鉤子的可靠性:JVM將在退出的時候盡最大努力來執行關閉鉤子,可是不保證必定會執行。例如,當在Linux中使用-kill命令時,或在Windows中終結進程時,因爲本地代碼被調用,JVM將當即退出或崩潰。
注意鉤子的時間消耗:須要注意的重點之一是,關閉鉤子不該該花費過多時間。考慮這樣一個場景,當用戶從操做系統中註銷,操做系統花費很是有限的時間就正常退出了,所以在這樣樣的場景下JVM也應該儘快退出。
結論:
Runtime.addShutdownHook(Thread hook)是很是方便的工具,它提供從JVM中優雅退出的一個通用機制。尤爲是在相似服務器實現這樣的大型應用中。固然,應該當心使用。
參考:
http://download.oracle.com/javase/1.5.0/docs/guide/lang/hook-design.html
原文地址:http://hellotojavaworld.blogspot.com/2010/11/runtimeaddshutdownhook.html