最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會(huì)員登陸 & 注冊(cè)

Java ShutDown Hook介紹和實(shí)例

2023-06-18 23:13 作者:我想問問天  | 我要投稿



# 概述:


之前有了解過Java的ShutDown Hook機(jī)制,但是因?yàn)闆]有使用場(chǎng)景也沒有深入學(xué)習(xí),最近剛好又看到ShutDown Hook的一些東西,想著學(xué)習(xí)總結(jié)一下,做下學(xué)習(xí)記錄。Java的Shutdown Hook是一種機(jī)制,允許開發(fā)者在Java虛擬機(jī)(JVM)即將關(guān)閉之前執(zhí)行一些清理或終止操作。Shutdown Hook提供了一個(gè)鉤子,允許開發(fā)者在JVM關(guān)閉時(shí)捕獲到關(guān)閉事件并執(zhí)行相應(yīng)的邏輯。以下是一些使用場(chǎng)景:


1. 資源釋放和清理:當(dāng)應(yīng)用程序結(jié)束或JVM關(guān)閉時(shí),可以使用Shutdown Hook來釋放和清理打開的文件、網(wǎng)絡(luò)連接、數(shù)據(jù)庫(kù)連接等資源。這確保資源在程序終止之前得到適當(dāng)?shù)年P(guān)閉,避免資源泄露和數(shù)據(jù)丟失。

2. 日志記錄和統(tǒng)計(jì):Shutdown Hook可以用于記錄應(yīng)用程序的關(guān)鍵統(tǒng)計(jì)信息或生成最終的日志報(bào)告。通過在JVM關(guān)閉前執(zhí)行這些操作,可以捕獲應(yīng)用程序在運(yùn)行期間的關(guān)鍵數(shù)據(jù),并生成相應(yīng)的日志記錄。

3. 緩存刷新:如果應(yīng)用程序使用了緩存機(jī)制,可以在JVM關(guān)閉前使用Shutdown Hook來刷新緩存,將緩存中的數(shù)據(jù)寫回到持久化存儲(chǔ)或其他目標(biāo)中,確保數(shù)據(jù)的持久化和一致性。

4. 任務(wù)終止和狀態(tài)保存:在某些情況下,可能需要在應(yīng)用程序終止時(shí)保存任務(wù)的當(dāng)前狀態(tài),以便在下次啟動(dòng)時(shí)恢復(fù)。通過在Shutdown Hook中執(zhí)行任務(wù)的狀態(tài)保存操作,可以將任務(wù)的狀態(tài)保存到持久化存儲(chǔ)中,并在下次啟動(dòng)時(shí)進(jìn)行恢復(fù)。

5. 線程管理:Shutdown Hook還可以用于管理和終止應(yīng)用程序中的線程。在JVM關(guān)閉前,可以使用Shutdown Hook發(fā)送終止信號(hào)給正在運(yùn)行的線程,以確保它們?cè)诮K止之前完成當(dāng)前任務(wù)并進(jìn)行清理操作。


上面說的這些使用場(chǎng)景,我都沒用到過,大家可以先了解一下對(duì)ShutDownHook有一個(gè)簡(jiǎn)單的認(rèn)識(shí)。




# 分析:


下面我們從源碼上去看一下,ShutDown Hook的方法和原理。


跟它相關(guān)的主要有兩個(gè)類ApplicationShutdownHooks和Runtime


```

class ApplicationShutdownHooks {

? ? /* The set of registered hooks */

? ? private static IdentityHashMap<Thread, Thread> hooks;

? ? static {

? ? ? ? try {

? ? ? ? ? ? Shutdown.add(1 /* shutdown hook invocation order */,

? ? ? ? ? ? ? ? false /* not registered if shutdown in progress */,

? ? ? ? ? ? ? ? new Runnable() {

? ? ? ? ? ? ? ? ? ? public void run() {

? ? ? ? ? ? ? ? ? ? ? ? runHooks();

? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? }

? ? ? ? ? ? );

? ? ? ? ? ? hooks = new IdentityHashMap<>();

? ? ? ? } catch (IllegalStateException e) {

? ? ? ? ? ? // application shutdown hooks cannot be added if

? ? ? ? ? ? // shutdown is in progress.

? ? ? ? ? ? hooks = null;

? ? ? ? }

? ? }



? ? private ApplicationShutdownHooks() {}


? ? /* Add a new shutdown hook.? Checks the shutdown state and the hook itself,

? ? ?* but does not do any security checks.

? ? ?*/

? ? 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);

? ? }


? ? /* Remove a previously-registered hook.? Like the add method, this method

? ? ?* does not do any security checks.

? ? ?*/

? ? static synchronized boolean remove(Thread hook) {

? ? ? ? if(hooks == null)

? ? ? ? ? ? throw new IllegalStateException("Shutdown in progress");


? ? ? ? if (hook == null)

? ? ? ? ? ? throw new NullPointerException();


? ? ? ? return hooks.remove(hook) != null;

? ? }


? ? /* Iterates over all application hooks creating a new thread for each

? ? ?* to run in. Hooks are run concurrently and this method waits for

? ? ?* them to finish.

? ? ?*/

? ? static void runHooks() {

? ? ? ? Collection<Thread> threads;

? ? ? ? synchronized(ApplicationShutdownHooks.class) {

? ? ? ? ? ? threads = hooks.keySet();

? ? ? ? ? ? hooks = null;

? ? ? ? }


? ? ? ? for (Thread hook : threads) {

? ? ? ? ? ? hook.start();

? ? ? ? }

? ? ? ? for (Thread hook : threads) {

? ? ? ? ? ? try {

? ? ? ? ? ? ? ? hook.join();

? ? ? ? ? ? } catch (InterruptedException x) { }

? ? ? ? }

? ? }

}

```




```

?-- Runtime.class里面的方法

?public void addShutdownHook(Thread hook) {

? ? ? ? SecurityManager sm = System.getSecurityManager();

? ? ? ? if (sm != null) {

? ? ? ? ? ? sm.checkPermission(new RuntimePermission("shutdownHooks"));

? ? ? ? }

? ? ? ? ApplicationShutdownHooks.add(hook);

? ? }


? ? public boolean removeShutdownHook(Thread hook) {

? ? ? ? SecurityManager sm = System.getSecurityManager();

? ? ? ? if (sm != null) {

? ? ? ? ? ? sm.checkPermission(new RuntimePermission("shutdownHooks"));

? ? ? ? }

? ? ? ? return ApplicationShutdownHooks.remove(hook);

? ? }



? ? public void halt(int status) {

? ? ? ? SecurityManager sm = System.getSecurityManager();

? ? ? ? if (sm != null) {

? ? ? ? ? ? sm.checkExit(status);

? ? ? ? }

? ? ? ? Shutdown.halt(status);

? ? }

```


結(jié)合這兩個(gè)類的源碼,我們可以看到添加Hook其實(shí)是往ApplicationShutdownHooks的靜態(tài)Map里面放入新的線程,但是這些線程只是創(chuàng)建后被保存了起來,只有當(dāng)程序退出時(shí),runHooks被執(zhí)行,每一個(gè)帶有Hook任務(wù)的線程才的start()方法才被執(zhí)行,也因?yàn)镠ook之間是相互獨(dú)立的線程,所以它們之間執(zhí)行是沒有順序的,而且因?yàn)橹骶€程調(diào)用了每個(gè)Hook的線程的join方法,所以主線程會(huì)等待Hook全部執(zhí)行完畢在退出。




##? 無法被添加的情況:


```

? ? 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");

```


1.ApplicationShutdownHooks已經(jīng)在調(diào)用Hook時(shí),hooks會(huì)置為null,不能在添加hook


2.Hook的Thread不能是已經(jīng)在運(yùn)行狀態(tài)的線程


3.因?yàn)閮?chǔ)存的Hook是根據(jù)線程是否相同來判斷的,所以同樣的Hook無法被添加




## 不適用的情況:


1.因?yàn)镾hutDown Hook只能處理正常退出的情況,kill -9這種是無法處理的


2Shutdown.halt和kill -9一樣都是強(qiáng)制退出,不會(huì)給Hook執(zhí)行的機(jī)會(huì)




# 使用:


下面放了一些簡(jiǎn)單的測(cè)試ShutDown的小例子,[github地址](https://github.com/wxwwt/java-practice/tree/master/src/main/java/com/scott/java/task/shutdown/hook):


```

?@Test

? ? public void test1() {

? ? ? ? // 測(cè)試正常退出的情況

? ? ? ? Runtime.getRuntime().addShutdownHook(

? ? ? ? ? ? ? ? new Thread(

? ? ? ? ? ? ? ? ? ? ? ? () -> {

? ? ? ? ? ? ? ? ? ? ? ? ? ? System.out.println("hook1 執(zhí)行了");

? ? ? ? ? ? ? ? ? ? ? ? })

? ? ? ? );

? ? }

? ??

? ? 輸出:hook1 執(zhí)行了

```


```

?@Test

? ? public void test2() {

? ? ? ? // 測(cè)試Hook執(zhí)行順序是否真的無序

? ? ? ? Runtime.getRuntime().addShutdownHook(

? ? ? ? ? ? ? ? new Thread(

? ? ? ? ? ? ? ? ? ? ? ? () -> {

? ? ? ? ? ? ? ? ? ? ? ? ? ? System.out.println("hook1 執(zhí)行了");

? ? ? ? ? ? ? ? ? ? ? ? })

? ? ? ? );


? ? ? ? Runtime.getRuntime().addShutdownHook(

? ? ? ? ? ? ? ? new Thread(

? ? ? ? ? ? ? ? ? ? ? ? () -> {

? ? ? ? ? ? ? ? ? ? ? ? ? ? System.out.println("hook2 執(zhí)行了");

? ? ? ? ? ? ? ? ? ? ? ? })

? ? ? ? );

? ? }

? ??

? ? 輸出:輸出結(jié)果hook1和hook2會(huì)隨機(jī)打印,沒有固定順序

```


```

?@Test

? ? public void test3() {

? ? ? ? // 測(cè)試kill -9 會(huì)執(zhí)行Hook嗎

? ? ? ? Runtime.getRuntime().addShutdownHook(

? ? ? ? ? ? ? ? new Thread(

? ? ? ? ? ? ? ? ? ? ? ? () -> {

? ? ? ? ? ? ? ? ? ? ? ? ? ? System.out.println("hook1 執(zhí)行了");

? ? ? ? ? ? ? ? ? ? ? ? })

? ? ? ? );


? ? ? ? while(true) {


? ? ? ? }


? ? }

? ??

? ? 輸出:

```


```

? ? @Test

? ? public void test4() {

? ? ? ? // 測(cè)試oom時(shí) 會(huì)執(zhí)行Hook嗎

? ? ? ? Runtime.getRuntime().addShutdownHook(

? ? ? ? ? ? ? ? new Thread(

? ? ? ? ? ? ? ? ? ? ? ? () -> {

? ? ? ? ? ? ? ? ? ? ? ? ? ? System.out.println("hook1 執(zhí)行了");

? ? ? ? ? ? ? ? ? ? ? ? })

? ? ? ? );


? ?

? ? ? ? List<Object> list = Lists.newArrayList();

? ? ? ? while(true) {

? ? ? ? ? ?list.add(new ShutDownHookTest());

? ? ? ? }

? ? }

? ??

? ? 輸出:

? ? ? java.lang.OutOfMemoryError: GC overhead limit exceeded


? ? ? ? ?at com.scott.java.task.shutdown.hook.ShutDownHookTest.test4(ShutDownHookTest.java:74)

? ? ? ? ?。。。省略不重要的日志


? ? ? ? ?hook1 執(zhí)行了

```


```

?@Test

? ? public void test5() {

? ? ? ? // 測(cè)試移除Hook后,會(huì)執(zhí)行Hook嗎

? ? ? ? Thread thread = new Thread(() -> {

? ? ? ? ? ? System.out.println("hook1 執(zhí)行了");

? ? ? ? });


? ? ? ? Runtime.getRuntime().addShutdownHook(thread);

? ? ? ? Runtime.getRuntime().removeShutdownHook(thread);

? ? }

? ??

? ? 輸出:

```


```

? ? @Test

? ? public void test6() {

? ? ? ? // 測(cè)試執(zhí)行halt方法后,會(huì)執(zhí)行Hook嗎

? ? ? ? Thread thread = new Thread(() -> {

? ? ? ? ? ? System.out.println("hook1 執(zhí)行了");

? ? ? ? });


? ? ? ? Runtime.getRuntime().addShutdownHook(thread);

? ? ? ? Runtime.getRuntime().halt(111);

? ? }

? ? 輸出:

```


```

?@Test

? ? public void test7() {

? ? ? ? // 測(cè)試已經(jīng)執(zhí)行Hook時(shí),還能添加新的hook嗎

? ? ? ? Thread thread = new Thread(() -> {

? ? ? ? ? ? System.out.println("hook1 執(zhí)行了");

? ? ? ? ? ? Run

? ? ? ? });


? ? ? ? Runtime.getRuntime().addShutdownHook(thread);

? ? ? ? Runtime.getRuntime().halt(111);

? ? }

? ? 輸出:

? ? hook1 執(zhí)行了

Exception in thread "Thread-0" java.lang.IllegalStateException: Shutdown in progress

```


```

? @Test

? ? public void test8() {

? ? ? ? // 測(cè)試重復(fù)注冊(cè)后,會(huì)執(zhí)行Hook嗎

? ? ? ? Thread thread = new Thread(() -> {

? ? ? ? ? ? System.out.println("hook1 執(zhí)行了");

? ? ? ? });


? ? ? ? Runtime.getRuntime().addShutdownHook(thread);

? ? ? ? Runtime.getRuntime().addShutdownHook(thread);

? ? }

? ??

? ? 輸出:java.lang.IllegalArgumentException: Hook previously registered

```


```

? @Test

? ? public void test9() {

? ? ? ? // 測(cè)試重復(fù)注冊(cè)后,會(huì)執(zhí)行Hook嗎

? ? ? ? Thread thread = new Thread(() -> {

? ? ? ? ? ? System.out.println("hook1 執(zhí)行了");

? ? ? ? });


? ? ? ? thread.start();

? ? ? ? Runtime.getRuntime().addShutdownHook(thread);

? ? }

? ??

? ? 輸出:

? ? ? ? hook1 執(zhí)行了


? ? java.lang.IllegalArgumentException: Hook already running

```




# 總結(jié)


1.ShutDown的使用還是比較簡(jiǎn)單,網(wǎng)上也有分析Spring和Dubbo等開源框架的使用例子,基本上都是用于銷毀處理資源釋放的問題


2.稍微要注意的就是一些特殊情況,比如hook執(zhí)行是無序的,不能重復(fù)添加相同的hook,已經(jīng)執(zhí)行的hook不能再創(chuàng)建新的hook等


3.平時(shí)基本沒用到過ShutDown Hook,自己想到一個(gè)比較有用的場(chǎng)景就是Jvm掛了,在Hook里面給監(jiān)控程序發(fā)通知發(fā)郵件之類的,讓技術(shù)人員來處理




# 參考資料


[1.oracle官網(wǎng)資料](https://docs.oracle.com/javase/8/docs/technotes/guides/lang/hook-design.html)


[2.Java Shutdown Hook 場(chǎng)景使用和源碼分析](https://segmentfault.com/a/1190000040167517)


[3.Adding Shutdown Hooks for JVM Applications](https://www.baeldung.com/jvm-shutdown-hooks)


Java ShutDown Hook介紹和實(shí)例的評(píng)論 (共 條)

分享到微博請(qǐng)遵守國(guó)家法律
巴青县| 新蔡县| 潞西市| 六枝特区| 陆川县| 南郑县| 扶沟县| 石景山区| 文水县| 岐山县| 宝坻区| 苏尼特右旗| 册亨县| 苗栗市| 化德县| 罗山县| 庆安县| 临武县| 沙坪坝区| 左贡县| 冕宁县| 呼玛县| 夏河县| 柞水县| 壤塘县| 华容县| 政和县| 冀州市| 张家口市| 麻阳| 黑水县| 漳浦县| 屏东县| 汕尾市| 大洼县| 共和县| 临湘市| 色达县| 临洮县| 卢湾区| 防城港市|