作者:京東物流 崔冬冬
一、先提出一個(gè)問(wèn)題
我們?nèi)绻贘VM退出的時(shí)候做一些事情,比如關(guān)閉遠(yuǎn)程鏈接,怎么實(shí)現(xiàn)呢?
二、ShutdownHook簡(jiǎn)介
java里有個(gè)方法Runtime.getRuntime#addShutdownHook,是否了解呢?
ShutdownHook是什么意思呢,看單詞解釋“關(guān)閉鉤子”,addShutdownHook就是添加一個(gè)關(guān)閉鉤子,這個(gè)鉤子是做什么的呢?能否解決上面的問(wèn)題?
1、RunTime類
先看一下看源碼RunTime#addShutdownHook方法與解釋。
1.1 方法解釋
核心意思,在Java虛擬機(jī)在關(guān)閉時(shí)會(huì)觸發(fā)一些自己添加的事件。
Registers a new virtual-machine shutdown hook. The Java virtual machine shuts down in response to two kinds of events: The program exits normally, when the last non-daemon thread exits or when the exit (equivalently, System.exit) method is invoked, or The virtual machine is terminated in response to a user interrupt, such as typing ^C, or a system-wide event, such as user logoff or system shutdown. A shutdown hook 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 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 exit method.
1.2 方法源碼
public void addShutdownHook(Thread hook) { @SuppressWarnings("removal") SecurityManager sm = System.getSecurityManager(); if (sm != null) { sm.checkPermission(new RuntimePermission("shutdownHooks")); } ApplicationShutdownHooks.add(hook); }
方法內(nèi)部調(diào)用了ApplicationShutdownHooks#add, 我們繼續(xù)往下看。
2、ApplicationShutdownHooks類
2.1 添加鉤子
private static IdentityHashMap 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); }
我們添加了一個(gè)鉤子,這個(gè)鉤子是個(gè)線程,這個(gè)線程怎么執(zhí)行的呢? 繼續(xù)看一下此類中的runHooks。
2.2 執(zhí)行鉤子
static void runHooks() { Collection threads; synchronized(ApplicationShutdownHooks.class) { threads = hooks.keySet(); hooks = null; } for (Thread hook : threads) { hook.start(); } for (Thread hook : threads) { while (true) { try { hook.join(); break; } catch (InterruptedException ignored) { } } } }
執(zhí)行runHooks的時(shí)候,會(huì)啟動(dòng)所有的hook線程,什么時(shí)候調(diào)用runHooks方法的呢?
2.3 執(zhí)行時(shí)機(jī)
為什么在系統(tǒng)退出的時(shí)候會(huì)執(zhí)行添加的hook呢?我們看一下正常的退出操作System#exit方法。
1) 類調(diào)用層級(jí)
System->Runtime->Shutdown->ApplicationShutdownHooks
2) 方法調(diào)用
系統(tǒng)退出入口:System#exit
步驟 1-->System#exit
步驟 2-->Runtime#exit;
步驟 3--> Shutdown#exit
步驟 4--> Shutdown#runHooks
步驟 5--> ApplicationShutdownHooks#runHooks
步驟 6-->啟動(dòng)添加的hook線程
3) 補(bǔ)充一下
為什么步驟4會(huì)調(diào)用到步驟5呢?
可以看一下ApplicationShutdownHooks的構(gòu)造函數(shù),在創(chuàng)建的時(shí)候,封裝了runHooks方法,放到了Shutdown的鉤子集合里。
如此形成閉環(huán),在系統(tǒng)正常退出的時(shí)候,最終執(zhí)行我們添加的hook。
三、舉個(gè)例子
了解了基本原理,我們看一下怎么使用的
public static void main(String[] args) throws InterruptedException { Thread thread = new Thread() { @Override public void run() { System.out.println("等等我"); } }; Runtime.getRuntime().addShutdownHook(thread); System.out.println("程序關(guān)閉"); } 輸出: 程序關(guān)閉 等等我
可以看到,在JVM退出的時(shí)候調(diào)用,執(zhí)行了此線程,我們開(kāi)發(fā)中,哪些場(chǎng)景可以使用呢?
四、應(yīng)用場(chǎng)景
關(guān)閉鏈接、線程、資源釋放、記錄執(zhí)行狀態(tài)等。
五、風(fēng)險(xiǎn)點(diǎn)
1、長(zhǎng)時(shí)間等待
如果添加的hook線程長(zhǎng)時(shí)間執(zhí)行,我們的退出命令會(huì)一直等待,為什么呢?
舉個(gè)例子,我們?cè)趫?zhí)行的時(shí)候sleep一下
public static void main(String[] args) throws InterruptedException { Thread thread = new Thread() { @Override public void run() { try { Thread.sleep(1000*300); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(new Date()+" 等我5分鐘"); } }; Runtime.getRuntime().addShutdownHook(thread); System.out.println(new Date()+" 程序關(guān)閉"); } 輸出: Tue Nov 12 17:37:38 CST 2024 程序關(guān)閉 Tue Nov 12 17:42:38 CST 2024 等我5分鐘
2、原因
JVM在退出的時(shí)候會(huì)調(diào)用runHooks方法,看一下上面的方法java.lang.ApplicationShutdownHooks#runHooks方法。
關(guān)鍵字 hook.join(); 主線程會(huì)等待子線程執(zhí)行完成。
如果程序一直執(zhí)行,不能退出怎么辦?
3、解決方案
1 ) 寫代碼時(shí)候控制執(zhí)行邏輯、時(shí)長(zhǎng)
kill -9 命令 強(qiáng)制退出
六、擴(kuò)展
1、Runtime.getRuntime#addShutdownHook是面向開(kāi)發(fā)者的
ApplicationShutdownHook#add、Shutdown#add我們都不能直接使用。
2、許多中間件框架也利用addShutdownHook來(lái)實(shí)現(xiàn)資源回收、清理等操作
比如Spring框架中,使用了ShutdownHook注冊(cè),我們常用的@PreDestroy在Bean銷毀前執(zhí)行一些操作,也是借助其回調(diào)的。
七、總結(jié)
1、本文簡(jiǎn)單介紹了一下ShutdownHook使用、原理、風(fēng)險(xiǎn)點(diǎn)。
2、我們工作中可以自己注冊(cè)ShutdownHook,主動(dòng)釋放一些資源,降低風(fēng)險(xiǎn)。
3、小知識(shí)分享,不足之處歡迎大家指正,關(guān)于java里的知識(shí)點(diǎn)也歡迎大家討論分享。
審核編輯 黃宇
-
JAVA
+關(guān)注
關(guān)注
19文章
2974瀏覽量
105131 -
JVM
+關(guān)注
關(guān)注
0文章
158瀏覽量
12259
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論