一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - 通過JDK源碼分析關閉鉤子詳解

通過JDK源碼分析關閉鉤子詳解

2021-02-01 11:49汪洋之舟---seaboat Java教程

一個簡單的關閉鉤子,程序被中斷或者正常退出時會顯示 hook shutdown!非常的優雅,有效,巧妙。那么這篇文章就來給大家介紹關于通過JDK源碼分析關閉鉤子的相關資料,文中通過示例代碼介紹的非常詳細,需要的朋友可以參考借鑒

關閉鉤子

用戶關閉關閉程序,需要做一些善后的清理工作,但問題是,某些用戶不會按照推薦的方法關閉應用程序,肯能導致善后工作無法進行。像tomcat調用server的start方法啟動容器,然后會逐級調用start。當發出關閉命令是會啟動關閉功能,但是關閉可能會有一些意外產生,導致應用程序沒有進入到我們制定的關閉方法去。如何解決這個問題呢,使得即使有意外也能正常進入關閉流程。

好在java提供了一種優雅的方式去解決這種問題。使得關閉的善后處理的代碼能執行。java的關閉鉤子能確保總是執行,無論用戶如何終止應用程序。除非用戶kill,這個是個死穴。

Java提供了Shutdown Hook機制,它讓我們在程序正常退出或者發生異常時能有機會做一些清場工作。使用的方法也很簡單,Java.Runtime.addShutdownHook(Thread hook)即可。關閉鉤子其實可以看成是一個已經初始化了的但還沒啟動的線程,當JVM關閉時會并發地執行注冊的所有關閉鉤子。

對java而言,虛擬機會對以下幾種操作進行關閉:

      (1)系統調用System.exit()方法

      (2)程序最后一個守護線程退出時,應用程序正常退出。

      (3)用戶強行中斷程序運行,比如ctrl+c等其他方式中斷java程序

關閉鉤子的生成:

    1.創建Thread的子類

    2.實現run方法,應用程序關閉時會調用該方法,不需要調用start方法

    3.在應用中實例化關閉鉤子類

    4.使用Runtime注冊關閉鉤子

鉤子執行時機

向JVM注冊關閉鉤子后的什么時候會被調用,什么時候不會被調用呢?分成以下情況:

  • Java程序正常運行完退出時會被調用。
  • windows和linux終端中通過ctrl-c終止命令時會被調用。
  • JVM發生OutOfMemory而退出時會被調用。
  • Java程序中執行System.exit()時會被調用。
  • 操作系統關閉時會被調用。
  • linux通過kill pid(除了kill -9 pid)結束進程時會被調用。
  • windows直接結束進程時不會被調用。

添加刪除鉤子

鉤子的添加和刪除都是通過 Runtime 來實現,里面的實現也比較簡單,可以看到 addShutdownHook 和 removeShutdownHook 方法都是先通過安全管理器先檢查是否有 shutdownHooks 的權限,然后再通過 ApplicationShutdownHooks 添加和刪除鉤子。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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);
 }

ApplicationShutdownHooks保管鉤子

ApplicationShutdownHooks 可以看成是用來保管所有關閉鉤子的容器,而主要是通過一個 IdentityHashMap

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

有了 hooks 這個變量,添加刪除鉤子就是直接向這個 HashMap 進行 put 和 remove 操作了,其中在操作前也會做一些檢查,比如添加鉤子前會做三個判斷:

      1. 所有鉤子是否已經開始執行了,hooks 為 null 即表示所有關閉鉤子已經開始執行,此時不能再添加了。

      2. 鉤子狀態是否為 alive ,是則表示鉤子已經在運行,不能添加了。

      3. 是否已經包含了該鉤子,已包含則不能再添加。

類似的判斷邏輯還有 remove 操作。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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);
}
 
 
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;
}

而 ApplicationShutdownHooks 中真正負責啟動所有鉤子的任務由 runHooks 方法負責,它的邏輯如下:

      1. 先對 ApplicationShutdownHooks 類加鎖并取到所有鉤子,然后將 hooks 變量設為 null 。

      2. 遍歷所有鉤子,分別啟動鉤子,前面有說到關閉鉤子其實可以看成是一個已經初始化了的但還沒啟動的線程,這里調用 start 方法將其啟動即可。

      3. 用 join 方法協調所有鉤子線程,等待他們執行完畢。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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) { }
  }
}

ApplicationShutdownHooks 的 runHooks 方法又是由誰負責調用的呢?如下,它其實是變成一個 Runnable 對象添加到 Shutdown 類中了,Runnable 的 run 方法負責調用 runHooks 方法。接下去就要看 Shutdown 類什么時候執行該 Runnable 對象了。

?
1
2
3
4
5
6
7
Shutdown.add(1 , false ,
        new Runnable() {
          public void run() {
            runHooks();
          }
        }
      );

Shutdown中的鉤子

ApplicationShutdownHooks 的 Runnable 對象添加到 Shutdown 中的邏輯如下,

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private static final int RUNNING = 0;
private static final int HOOKS = 1;
private static final int FINALIZERS = 2;
 
private static final int MAX_SYSTEM_HOOKS = 10;
private static final Runnable[] hooks = new Runnable[MAX_SYSTEM_HOOKS];
 
static void add(int slot, boolean registerShutdownInProgress, Runnable hook) {
    synchronized (lock) {
      if (hooks[slot] != null)
        throw new InternalError("Shutdown hook at slot " + slot + " already registered");
 
      if (!registerShutdownInProgress) {
        if (state > RUNNING)
          throw new IllegalStateException("Shutdown in progress");
      } else {
        if (state > HOOKS || (state == HOOKS && slot <= currentRunningHook))
          throw new IllegalStateException("Shutdown in progress");
      }
 
      hooks[slot] = hook;
    }
  }

slot表示將Runnable對象賦給 hooks 數組中的哪個元素中, Shutdown 中同樣有一個 hooks 變量,它是 Runnable[] 類型,長度為 MAX_SYSTEM_HOOKS ,即為 10 。這個數組可以看成是鉤子的優先級實現,數組下標用于表示優先級,slot = 1 則表示賦值到數組中第二個元素。

registerShutdownInProgress 表示是否允許注冊鉤子,即使正在執行 shutdown 。前面傳入 false ,顯然是不允許。其中 state > RUNNING 條件表示其他狀態都要拋出異常,除非是 RUNNING 狀態,這個很好理解,一共有三個狀態,RUNNING、HOOKS、FINALIZERS,值分別為0、1、2。如果 registerShutdownInProgress 為 true 則只要不為 FINALIZERS 狀態,同時 slot 也要大于當前鉤子數組的下標即可。

在前面說到的鉤子執行時機的情況下,JVM都會調用到 Shutdown 類的 sequence 方法,如下,

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
private static void sequence() {
    synchronized (lock) {
      if (state != HOOKS) return;
    }
    runHooks();
    boolean rfoe;
    synchronized (lock) {
      state = FINALIZERS;
      rfoe = runFinalizersOnExit;
    }
    if (rfoe) runAllFinalizers();
  }
 
private static void runHooks() {
    for (int i=0; i < MAX_SYSTEM_HOOKS; i++) {
      try {
        Runnable hook;
        synchronized (lock) {
          currentRunningHook = i;
          hook = hooks[i];
        }
        if (hook != null) hook.run();
      } catch(Throwable t) {
        if (t instanceof ThreadDeath) {
          ThreadDeath td = (ThreadDeath)t;
          throw td;
        }
      }
    }
  }

首先判斷當前狀態不等于 HOOKS 則直接返回,接著執行 runHooks 方法,這個方法也是我們主要要看的方法。然后再將狀態設為 FINALIZERS ,最后如果需要的話還要調用 runAllFinalizers 方法執行所有 finalizer。所以在JVM關閉時 runHooks 方法是會被調用的。

runHooks 方法邏輯簡單,就是遍歷 Runnable 數組,一個個調用其 run 方法讓其執行。

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對服務器之家的支持。

原文鏈接:http://blog.csdn.net/wangyangzhizhou/article/details/78315757

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 男同桌扒开女同桌胸罩喝奶 | 44444色视频在线观看 | 爽好大快深点一视频 | 精品久久久久久亚洲 | 男人女人性生活视频 | 奇米影视在线观看 | 成人au免费视频影院 | 国产一级黄色录像 | 国产精品久久久久久久久免费 | 91精品啪在线观看国产日本 | 亚洲另类第一页 | 2019年国产不卡在线刷新 | 国内亚州视频在线观看 | 亚洲视频在线观看免费视频 | 国产精品边做边接电话在线观看 | 莫莉瑞典1977k | 色四虎 | 黑人性xxxⅹxxbbbbb | 日本海鸣馆 | 无套啪啪| 久久国产精品无码视欧美 | 我的绝色岳每雯雯 | 亚洲丰满模特裸做爰 | 热99re久久精品精品免费 | 美女和男人免费网站视频 | 午夜伦伦电影理论片费看 | 国产免费看片 | 乳女教师欲乱动漫无修版动画3d | 歪歪私人影院成人毛片 | 亚洲精品无码不卡在线观看 | 色综合久久综合网欧美综合网 | 天天操天天干天天舔 | 狠狠色狠狠色综合系列 | 青青青国产精品国产精品久久久久 | japanese秘书丝袜 | 亚洲成人网页 | 男生操女生漫画 | 日本高清免费中文字幕不卡 | 青青在线 | 欧美老肥妇bbb | 2021海角社区最新版 |