最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

通過JDK源碼分析關(guān)閉鉤子詳解

 更新時(shí)間:2017年11月08日 10:14:51   作者:汪洋之舟---seaboat  
一個(gè)簡單的關(guān)閉鉤子,程序被中斷或者正常退出時(shí)會(huì)顯示 hook shutdown!非常的優(yōu)雅,有效,巧妙。那么這篇文章就來給大家介紹關(guān)于通過JDK源碼分析關(guān)閉鉤子的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考借鑒。

關(guān)閉鉤子

用戶關(guān)閉關(guān)閉程序,需要做一些善后的清理工作,但問題是,某些用戶不會(huì)按照推薦的方法關(guān)閉應(yīng)用程序,肯能導(dǎo)致善后工作無法進(jìn)行。像tomcat調(diào)用server的start方法啟動(dòng)容器,然后會(huì)逐級(jí)調(diào)用start。當(dāng)發(fā)出關(guān)閉命令是會(huì)啟動(dòng)關(guān)閉功能,但是關(guān)閉可能會(huì)有一些意外產(chǎn)生,導(dǎo)致應(yīng)用程序沒有進(jìn)入到我們制定的關(guān)閉方法去。如何解決這個(gè)問題呢,使得即使有意外也能正常進(jìn)入關(guān)閉流程。

好在java提供了一種優(yōu)雅的方式去解決這種問題。使得關(guān)閉的善后處理的代碼能執(zhí)行。java的關(guān)閉鉤子能確??偸菆?zhí)行,無論用戶如何終止應(yīng)用程序。除非用戶kill,這個(gè)是個(gè)死穴。

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

對(duì)java而言,虛擬機(jī)會(huì)對(duì)以下幾種操作進(jìn)行關(guān)閉:

      (1)系統(tǒng)調(diào)用System.exit()方法

      (2)程序最后一個(gè)守護(hù)線程退出時(shí),應(yīng)用程序正常退出。

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

關(guān)閉鉤子的生成:

    1.創(chuàng)建Thread的子類

    2.實(shí)現(xiàn)run方法,應(yīng)用程序關(guān)閉時(shí)會(huì)調(diào)用該方法,不需要調(diào)用start方法

    3.在應(yīng)用中實(shí)例化關(guān)閉鉤子類

    4.使用Runtime注冊關(guān)閉鉤子

鉤子執(zhí)行時(shí)機(jī)

向JVM注冊關(guān)閉鉤子后的什么時(shí)候會(huì)被調(diào)用,什么時(shí)候不會(huì)被調(diào)用呢?分成以下情況:

  • Java程序正常運(yùn)行完退出時(shí)會(huì)被調(diào)用。
  • windows和linux終端中通過ctrl-c終止命令時(shí)會(huì)被調(diào)用。
  • JVM發(fā)生OutOfMemory而退出時(shí)會(huì)被調(diào)用。
  • Java程序中執(zhí)行System.exit()時(shí)會(huì)被調(diào)用。
  • 操作系統(tǒng)關(guān)閉時(shí)會(huì)被調(diào)用。
  • linux通過kill pid(除了kill -9 pid)結(jié)束進(jìn)程時(shí)會(huì)被調(diào)用。
  • windows直接結(jié)束進(jìn)程時(shí)不會(huì)被調(diào)用。

添加刪除鉤子

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

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 可以看成是用來保管所有關(guān)閉鉤子的容器,而主要是通過一個(gè) IdentityHashMap

private static IdentityHashMap<Thread, Thread> hooks;

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

      1. 所有鉤子是否已經(jīng)開始執(zhí)行了,hooks 為 null 即表示所有關(guān)閉鉤子已經(jīng)開始執(zhí)行,此時(shí)不能再添加了。

      2. 鉤子狀態(tài)是否為 alive ,是則表示鉤子已經(jīng)在運(yùn)行,不能添加了。

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

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

  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 中真正負(fù)責(zé)啟動(dòng)所有鉤子的任務(wù)由 runHooks 方法負(fù)責(zé),它的邏輯如下:

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

      2. 遍歷所有鉤子,分別啟動(dòng)鉤子,前面有說到關(guān)閉鉤子其實(shí)可以看成是一個(gè)已經(jīng)初始化了的但還沒啟動(dòng)的線程,這里調(diào)用 start 方法將其啟動(dòng)即可。

      3. 用 join 方法協(xié)調(diào)所有鉤子線程,等待他們執(zhí)行完畢。

  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 方法又是由誰負(fù)責(zé)調(diào)用的呢?如下,它其實(shí)是變成一個(gè) Runnable 對(duì)象添加到 Shutdown 類中了,Runnable 的 run 方法負(fù)責(zé)調(diào)用 runHooks 方法。接下去就要看 Shutdown 類什么時(shí)候執(zhí)行該 Runnable 對(duì)象了。

Shutdown.add(1 , false ,
        new Runnable() {
          public void run() {
            runHooks();
          }
        }
      );

Shutdown中的鉤子

ApplicationShutdownHooks 的 Runnable 對(duì)象添加到 Shutdown 中的邏輯如下,

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對(duì)象賦給 hooks 數(shù)組中的哪個(gè)元素中, Shutdown 中同樣有一個(gè) hooks 變量,它是 Runnable[] 類型,長度為 MAX_SYSTEM_HOOKS ,即為 10 。這個(gè)數(shù)組可以看成是鉤子的優(yōu)先級(jí)實(shí)現(xiàn),數(shù)組下標(biāo)用于表示優(yōu)先級(jí),slot = 1 則表示賦值到數(shù)組中第二個(gè)元素。

registerShutdownInProgress 表示是否允許注冊鉤子,即使正在執(zhí)行 shutdown 。前面?zhèn)魅?false ,顯然是不允許。其中 state > RUNNING 條件表示其他狀態(tài)都要拋出異常,除非是 RUNNING 狀態(tài),這個(gè)很好理解,一共有三個(gè)狀態(tài),RUNNING、HOOKS、FINALIZERS,值分別為0、1、2。如果 registerShutdownInProgress 為 true 則只要不為 FINALIZERS 狀態(tài),同時(shí) slot 也要大于當(dāng)前鉤子數(shù)組的下標(biāo)即可。

在前面說到的鉤子執(zhí)行時(shí)機(jī)的情況下,JVM都會(huì)調(diào)用到 Shutdown 類的 sequence 方法,如下,

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;
        }
      }
    }
  }

首先判斷當(dāng)前狀態(tài)不等于 HOOKS 則直接返回,接著執(zhí)行 runHooks 方法,這個(gè)方法也是我們主要要看的方法。然后再將狀態(tài)設(shè)為 FINALIZERS ,最后如果需要的話還要調(diào)用 runAllFinalizers 方法執(zhí)行所有 finalizer。所以在JVM關(guān)閉時(shí) runHooks 方法是會(huì)被調(diào)用的。

runHooks 方法邏輯簡單,就是遍歷 Runnable 數(shù)組,一個(gè)個(gè)調(diào)用其 run 方法讓其執(zhí)行。

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問大家可以留言交流,謝謝大家對(duì)腳本之家的支持。

相關(guān)文章

  • idea中cherry pick的用法

    idea中cherry pick的用法

    Cherry-Pick可以將一個(gè)分支的某些commit,合并到另一個(gè)分支,本文給大家分享idea中cherry pick的用法,感興趣的朋友跟隨小編一起看看吧
    2023-08-08
  • Spring?Boot提高開發(fā)效率必備工具lombok使用

    Spring?Boot提高開發(fā)效率必備工具lombok使用

    這篇文章主要為大家介紹了Spring?Boot提高開發(fā)效率的必備工具lombok使用方法示例及步驟說明,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2022-03-03
  • Spring中的事務(wù)管理實(shí)例詳解

    Spring中的事務(wù)管理實(shí)例詳解

    這篇文章主要介紹了Spring中的事務(wù)管理,以實(shí)例形式詳細(xì)分析了事務(wù)的概念與特性以及事物管理的具體用法,需要的朋友可以參考下
    2014-11-11
  • 用IDEA創(chuàng)建SpringBoot項(xiàng)目的詳細(xì)步驟記錄

    用IDEA創(chuàng)建SpringBoot項(xiàng)目的詳細(xì)步驟記錄

    Idea有著非常簡便的Spring Boot新建過程,同時(shí)依靠pom自動(dòng)下載依賴,下面這篇文章主要給大家介紹了關(guān)于用IDEA創(chuàng)建SpringBoot項(xiàng)目的詳細(xì)步驟,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2022-08-08
  • Springboot整合SpringSecurity的完整案例詳解

    Springboot整合SpringSecurity的完整案例詳解

    Spring Security是基于Spring生態(tài)圈的,用于提供安全訪問控制解決方案的框架,Spring Security登錄認(rèn)證主要涉及兩個(gè)重要的接口 UserDetailService和UserDetails接口,本文對(duì)Springboot整合SpringSecurity過程給大家介紹的非常詳細(xì),需要的朋友參考下吧
    2024-01-01
  • SpringBoot實(shí)現(xiàn)自定義配置文件提示的方法

    SpringBoot實(shí)現(xiàn)自定義配置文件提示的方法

    這篇文章主要介紹了SpringBoot實(shí)現(xiàn)自定義配置文件提示的方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-03-03
  • 詳解Java關(guān)于JDK中時(shí)間日期的API

    詳解Java關(guān)于JDK中時(shí)間日期的API

    這篇文章主要介紹了詳解Java關(guān)于JDK中時(shí)間日期的API,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-09-09
  • java獲取日期之間天數(shù)的方法

    java獲取日期之間天數(shù)的方法

    這篇文章主要介紹了java獲取日期之間天數(shù)的方法,涉及java針對(duì)日期的轉(zhuǎn)換與數(shù)學(xué)運(yùn)算相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-07-07
  • Spring?中?PageHelper?不生效問題及解決方法

    Spring?中?PageHelper?不生效問題及解決方法

    這篇文章主要介紹了Spring?中?PageHelper?不生效問題,使用這個(gè)插件時(shí)要注意版本的問題,不同的版本可能 PageHelper 不會(huì)生效,本文結(jié)合示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-12-12
  • Java異常分類以及幾種處理機(jī)制分析講解

    Java異常分類以及幾種處理機(jī)制分析講解

    在Java的廣闊宇宙中,有一群特殊的“超級(jí)英雄”,它們在代碼世界中穿梭,守護(hù)著程序的正常運(yùn)行——它們就是“異常”,這些英雄們,各司其職,保護(hù)著程序免受錯(cuò)誤的侵?jǐn)_,今天,我們將深入這個(gè)神秘的世界,全面解析異常的分類,掌握異常的處理機(jī)制
    2024-07-07

最新評(píng)論

罗山县| 讷河市| 班戈县| 娄烦县| 龙山县| 三门峡市| 宣恩县| 通道| 舟山市| 富裕县| 汉阴县| 榆林市| 临夏县| 萨嘎县| 萨嘎县| 金秀| 五家渠市| 出国| 吴堡县| 宜丰县| 乌苏市| 湟源县| 隆回县| 吉林市| 铜陵市| 会东县| 泸定县| 广宁县| 方山县| 义乌市| 通化市| 怀远县| 罗源县| 赤城县| 五寨县| 大名县| 女性| 固原市| 五家渠市| 汽车| 阳山县|