国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

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

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

延伸 · 閱讀

精彩推薦
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25 Weibo Article 26 Weibo Article 27 Weibo Article 28 Weibo Article 29 Weibo Article 30 Weibo Article 31 Weibo Article 32 Weibo Article 33 Weibo Article 34 Weibo Article 35 Weibo Article 36 Weibo Article 37 Weibo Article 38 Weibo Article 39 Weibo Article 40
主站蜘蛛池模板: 久久天天躁狠狠躁夜夜免费观看 | 国产美女久久 | 国产成人精品一区二区在线 | 欧美精品一区二区三区一线天视频 | 欧美一级片在线观看 | 国产精品亚洲一区二区三区在线 | 午夜窝窝 | 中国女人黄色大片 | 日韩成人在线一区 | 青青久久| av在线天堂网 | 成人h动漫在线看 | 99精品欧美一区二区蜜桃免费 | 偷偷干夜夜拍 | 极品一区 | 亚洲欧美精品 | 精品视频网 | 国产一区二区在线免费观看 | 精品国产影院 | 亚洲精品免费视频 | 免费成人黄色 | www.日韩.com| 亚洲日韩中文字幕一区 | 精品久久久久一区二区国产 | 黄频免费在线观看 | 精久久| 久久99精品国产自在现线 | 色九九 | 欧美精品成人 | 亚洲激情一区 | 久久精品一| 久久视频在线看 | 北条麻妃99精品青青久久主播 | 中文字幕在线视频观看 | 国产一区 欧美 | 午夜免费在线 | 老黄网站在线观看 | 91免费观看视频 | 51ⅴ精品国产91久久久久久 | 艹逼逼视频 | 真实的国产乱xxxx在线 |