您好,登錄后才能下訂單哦!
今天就跟大家聊聊有關ShutdownHook如何讓應用優雅的停止,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。
對于應用,停止的時候一般都會把環境和數據恢復到運行前的樣子,和單元測試時tearDown要做的效果基本類似。而為了實現停止時恢復現場,英文中有個特定的描述:
shutdown gracefully
在Java中,為了實現gracefully shutdown,有一個特定的接口可供使用,就是我們今天要提到的shutdown hook。它與回調函數功能類似,文檔中對其描述如下:
關閉鉤子 只是一個已初始化但尚未啟動的線程。虛擬機開始啟用其關閉序列時,它會以某種未指定的順序啟動所有已注冊的關閉鉤子,并讓它們同時運行。運行完所有的鉤子后,如果已啟用退出終結,那么虛擬機接著會運行所有未調用的終結方法。最后,虛擬機會暫停。注意,關閉序列期間會繼續運行守護線程,如果通過調用
exit
方法來發起關閉序列,那么也會繼續運行非守護線程。
要為應用添加shutdownHook,需要做的只是這樣的下操作:
Runtime.getRuntime().addShutdownHook(new Thread() {
public void run() { /* my shutdown code here */ } });
向addShutdownHook方法傳入的Thread,其run方法即為自定義的shutdown時清理邏輯。
JDK內部,是通過一個Map來保存所有添加的ShutdownHook,在被觸發時執行。
public void addShutdownHook(Thread hook) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("shutdownHooks"));
}
ApplicationShutdownHooks.add(hook);static synchronized void add(Thread hook) {
hooks.put(hook, hook);
}private static IdentityHashMap<Thread, Thread> hooks;hooks = new IdentityHashMap<>();
那這個shutdownHook一般是在什么時候會被調用呢?
我們看JDK的文檔中描述,對于兩類事件Java虛擬機會退出:
Java 虛擬機會為了響應以下兩類事件而關閉:
程序正常退出,這發生在最后的非守護線程退出時,或者在調用
exit
(等同于System.exit
)方法時。或者,為響應用戶中斷而終止 虛擬機,如鍵入 ^C;或發生系統事件,比如用戶注銷或系統關閉。
在Java虛擬機退出的時候,這些設置的shutdownHook會被并行的調用。但需要注意的是,對于非正常方式退出Java虛擬機,例如殺進程,系統斷電等,這些情況下,shutdownHook不會被執行。
我們來看,在Tomcat內部,是如何使用ShutdownHook的。
Catalina類內部,在服務器內部各個組件啟動完畢后,有這樣一段代碼
// Register shutdown hook
if (useShutdownHook) {
if (shutdownHook == null) {
shutdownHook = new CatalinaShutdownHook();
}
Runtime.getRuntime().addShutdownHook(shutdownHook);}
和我們在本文開頭看到的一樣,他注冊了一個ShutdownHook。這個hook的內容如下:
/**
* Shutdown hook which will perform a clean shutdown of Catalina if needed.
*/
protected class CatalinaShutdownHook extends Thread {
public void run() {
try {
if (getServer() != null) {
Catalina.this.stop();
}
} catch (Throwable ex) {
} finally {
}
}
}
看就是在shutdown的時候通過調用應用服務器的stop方法,來shutdown gracefully。
而我們一般停止Tomcat會通過調用shutdown腳本的方式進行,這個時候,腳本實質上去執行了應用服務器的stop方法來進行停止,而不是被shutdownHook來反調過來的。
因此,在讀源碼時,你會發現代碼中有這樣的內容:
/**
* Stop an existing server instance.
*/
public void stop() {
try {
// Remove the ShutdownHook first so that server.stop()
// doesn't get invoked twice
if (useShutdownHook) {
Runtime.getRuntime().removeShutdownHook(shutdownHook);
} catch (Throwable t) {
ExceptionUtils.handleThrowable(t);}
我們看到,在停止Server的時候,會先執行removeShutdownHook的操作,注釋里寫的明白,是為了防止server和stop被執行兩次。
哪種情況下會被執行兩次呢?
當使用catalina的shutdown腳本停止Server時,這個方法并不是從shutdownHook調用過來,此時,shutdownHook還沒有執行,所以在JVM退出的時候,shutdownHook會被觸發。如果此處還沒去remove掉的話,就還會調用過來。這一點在我們自己開發應用時需要注意借鑒一下。
看到這里,希望你不要說然并卵。
當然,如果你已經脫口而出,那...
我找了例子證明,你已經在不知不覺中使用了shutdownHook,例如你在輸出日志的時候,以下代碼是JDK的LogManager中的一部分,我們看到,其構造方法中直接會添加一個名為Cleaner的shutdownHook。
private LogManager(Void checked) {
// Add a shutdown hook to close the global handlers.
try {
Runtime.getRuntime().addShutdownHook(new Cleaner());
} catch (IllegalStateException e) {
// If the VM is already shutting down,
// We do not need to register shutdownHook.
}
}public void run() {
// This is to ensure the LogManager.<clinit> is completed
// before synchronized block. Otherwise deadlocks are possible.
LogManager mgr = manager;
// Do a reset to close all active handlers.
reset();
}
我們自己添加shutdownHook的時候,需要注意的是:
在內部不要寫耗時的操作,也不要寫容易引起死鎖的操作。多個ShutdownHook之間如果存在資源競爭而死鎖,那應用就停止不了了。
看完上述內容,你們對ShutdownHook如何讓應用優雅的停止有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。