天天看點

異常處理器詳解 Java多線程異常處理機制 多線程中篇(四)

在Thread中有異常處理器相關的方法

異常處理器詳解 Java多線程異常處理機制 多線程中篇(四)

在ThreadGroup中也有相關的異常處理方法

異常處理器詳解 Java多線程異常處理機制 多線程中篇(四)

示例

未檢查異常

異常處理器詳解 Java多線程異常處理機制 多線程中篇(四)

對于未檢查異常,将會直接宕掉,主線程則繼續運作,程式會繼續運作

在主線程中能不能捕獲呢?

我們簡單粗暴一點,直接全部包到try catch中

異常處理器詳解 Java多線程異常處理機制 多線程中篇(四)

你會發現,然而并沒有什麼卵用,主線程中的try catch并不會得到什麼資訊,跟原來的結果還是一樣的,線程直接宕掉

已檢查異常

異常處理器詳解 Java多線程異常處理機制 多線程中篇(四)

對于已檢查的異常,run方法本身是不支援抛出的,上面代碼中,想要throws,IDE提示異常,從run方法可以看得出來

run方法本身是不支援throws的(簽名中沒有throws)

異常處理器詳解 Java多線程異常處理機制 多線程中篇(四)

是以怎麼辦?

既然是已檢查異常,肯定是要處理的,既然不能丢出去,就隻有一個辦法了,那就是自己捕獲,放置在try catch中

異常處理器詳解 Java多線程異常處理機制 多線程中篇(四)

小結

在run方法中是不能夠抛出異常的,如果是已檢查的異常,那麼必須進行try catch

對于未檢查的異常,如果沒有進行處理,一旦抛出線程将會宕掉,而且在主線程中并不能捕獲到這個異常

難道對于未檢查的異常也都是try catch嗎?(當然,這是一種方式)

還有沒有其他解決方案?

異常處理器

在Java線程的run方法中,對于未檢查異常,借助于異常處理器進行處理的

字面意思,直接了解為處理異常的方法,那麼如何配置這個處理異常的方法呢?如何設定,又是如何調用?

UncaughtExceptionHandler,是Thread的内部接口(1.8中已經設定為函數式接口)

異常處理器詳解 Java多線程異常處理機制 多線程中篇(四)

Thread内部有兩個變量,用于記錄異常處理器

異常處理器詳解 Java多線程異常處理機制 多線程中篇(四)

對于兩個set方法,沒有什麼特别的,主要就是設定這兩個内部變量

異常處理器詳解 Java多線程異常處理機制 多線程中篇(四)

對于getUncaughtExceptionHandler方法,如果目前非空,那麼傳回目前,否則,将傳回目前線程組,很顯然,ThreadGroup實作了Thread.UncaughtExceptionHandler

異常處理器詳解 Java多線程異常處理機制 多線程中篇(四)

對于getDefaultUncaughtExceptionHandler,這是簡單的傳回内部變量

異常處理器詳解 Java多線程異常處理機制 多線程中篇(四)

此時我們大緻了解到了這幾個方法,内部有兩個UncaughtExceptionHandler異常處理器,分别都有getter和setter方法

setter方法都是直接設定

getDefaultUncaughtExceptionHandler是直接擷取

getUncaughtExceptionHandler如果非空那麼直接擷取,否則将會傳回目前線程組,目前線程組也實作了Thread.UncaughtExceptionHandler,内部實作了方法public void uncaughtException(Thread t, Throwable e)

換句話說,線程組内部實作了一個線程處理器

兩個處理器含義

我們看到了表面的樣子,但是這兩個内部變量到底幹嘛的?

對于defaultUncaughtExceptionHandler,表示的是應用程式預設的,應用程式預設的,也就是整個程式使用的,可以看得到,對于他的getter和setter以及自身,都是static修飾的

對于uncaughtExceptionHandler,屬于執行個體方法,也就是說每個線程可以擁有一個

簡言之:每個線程都可以有一個uncaughtExceptionHandler,整個應用可以有一個defaultUncaughtExceptionHandler

全局和個體的關系,就如同我們平時見到的其他概念一樣,如果單獨設定了,那麼就使用自己的,如果沒有設定就走全局的

既可以單獨設定,又可以全局設定(沒有設定的才會走全局),既可以保障靈活性,有能夠對于那些沒設定的提供統一配置,比如統一将異常資訊寫入檔案等,也有諸多應用場景與好處

異常處理器處理邏輯

當異常發生時,JVM會調用異常分發處理器,也就是借助于getUncaughtExceptionHandler方法,擷取異常處理器,然後執行他的uncaughtException方法

第一個參數就是目前線程this,第二個參數就是異常對象

看注釋:JVM調用

異常處理器詳解 Java多線程異常處理機制 多線程中篇(四)

是以關鍵點在于getUncaughtExceptionHandler傳回什麼異常處理器,我們再回過頭來看下源代碼

異常處理器詳解 Java多線程異常處理機制 多線程中篇(四)

如果已經設定,那麼将會直接傳回;

如果沒有設定,将會傳回目前線程組(前面說了ThreadGroup實作了Thread.UncaughtExceptionHandler)

當調用ThreadGroup的uncaughtException方法時,如上圖下半部分

如果他的父線程組重寫了uncaughtException方法,那麼将會調用他的父線程組的方法,如果父親節點沒有重寫,爺爺節點重寫了将會調用爺爺的,以此類推

但是如果所有的祖先線程組都沒有重寫呢?很顯然,所有的方法代碼都是上面這樣子的(上圖下半部分),将會遞歸到***線程組,然後不滿足parent,然後走到else,這中間什麼有意義的事情都沒有做

在else中,會首先擷取應用預設的異常處理器,如果仍舊是沒有設定

不好意思,直接轉到system.err了

異常處理器詳解 Java多線程異常處理機制 多線程中篇(四)

代碼示例

異常處理器詳解 Java多線程異常處理機制 多線程中篇(四)

從上面的示例可以看得出來,盡管仍就出現了異常,我們能夠進行資訊擷取與感覺,不會直接宕掉了

如果先start,然後在設定異常處理器會發生什麼?

異常處理器詳解 Java多線程異常處理機制 多線程中篇(四)

可以看得到,線程仍舊是直接宕掉,異常處理器無效,是以setUncaughtExceptionHandler方法必須在start方法前調用!

總結

在Thread中的run方法,不能夠抛出異常,隻能進行捕獲

  • 對于已檢查異常,必須捕獲
  • 對于未檢查異常,你也可以進行try catch,但是代碼始終包裹在try中,真的好嗎?
  • 還另外提供了異常處理器機制用于處理未檢查異常

有兩種異常處理器:

線程自身的處理器和全局的異常處理器

  1. 如果設定了異常處理器uncaughtExceptionHandler,那麼将會使用這個
  2. 如果沒設定,将會在祖先線程組中查找第一個重寫了uncaughtException的線程組,然後調用他的uncaughtException方法
  3. 如果都沒有重寫,那麼使用應用預設的全局異常處理器defaultUncaughtExceptionHandler
  4. 如果還是沒有設定,直接标準錯誤列印資訊

如果想要設定自己的異常處理器,可以通過對應的setter方法進行設定,如果想要設定全局的可以調用靜态方法進行設定

異常處理器Thread.UncaughtExceptionHandler是一個函數式接口,是以後續,你可以使用Lambda表達式直接編寫,大大減少了工作量

原文位址:異常處理器詳解 Java多線程異常處理機制 多線程中篇(四)