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


在Thread中有異常處理器相關的方法
image_5c5bc46a_7657
在ThreadGroup中也有相關的異常處理方法
image_5c5bc46a_717e

示例

未檢查異常

image_5c5bc46a_25c1
對於未檢查異常,將會直接宕掉,主線程則繼續運行,程序會繼續運行
在主線程中能不能捕獲呢?
我們簡單粗暴一點,直接全部包到try catch中
image_5c5bc46a_1f01
你會發現,然而並沒有什么卵用,主線程中的try catch並不會得到什么信息,跟原來的結果還是一樣的,線程直接宕掉

已檢查異常

image_5c5bc46a_1b9a
對於已檢查的異常,run方法本身是不支持拋出的,上面代碼中,想要throws,IDE提示異常,從run方法可以看得出來
run方法本身是不支持throws的(簽名中沒有throws)
image_5c5bc46a_2d74
所以怎么辦?
既然是已檢查異常,肯定是要處理的,既然不能丟出去,就只有一個辦法了,那就是自己捕獲,放置在try catch中
image_5c5bc46a_5ee4

小結

在run方法中是不能夠拋出異常的,如果是已檢查的異常,那么必須進行try catch
對於未檢查的異常,如果沒有進行處理,一旦拋出線程將會宕掉,而且在主線程中並不能捕獲到這個異常
難道對於未檢查的異常也都是try catch嗎?(當然,這是一種方式)
還有沒有其他解決方案?

異常處理器

在Java線程的run方法中,對於未檢查異常,借助於異常處理器進行處理的
字面意思,直接理解為處理異常的方法,那么如何配置這個處理異常的方法呢?如何設置,又是如何調用?
UncaughtExceptionHandler,是Thread的內部接口(1.8中已經設置為函數式接口)
image_5c5bc46a_5bcf
Thread內部有兩個變量,用於記錄異常處理器
image_5c5bc46a_1ec8
對於兩個set方法,沒有什么特別的,主要就是設置這兩個內部變量
image_5c5bc46a_205e
對於getUncaughtExceptionHandler方法,如果當前非空,那么返回當前,否則,將返回當前線程組,很顯然,ThreadGroup實現了Thread.UncaughtExceptionHandler
image_5c5bc46b_1509
對於getDefaultUncaughtExceptionHandler,這是簡單的返回內部變量
image_5c5bc46b_7ff5
此時我們大致了解到了這幾個方法,內部有兩個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調用
image_5c5bc46b_4da
 
所以關鍵點在於getUncaughtExceptionHandler返回什么異常處理器,我們再回過頭來看下源代碼
image_5c5bc46b_3e2
如果已經設置,那么將會直接返回;
如果沒有設置,將會返回當前線程組(前面說了ThreadGroup實現了Thread.UncaughtExceptionHandler)
當調用ThreadGroup的uncaughtException方法時,如上圖下半部分
如果他的父線程組重寫了uncaughtException方法,那么將會調用他的父線程組的方法,如果父親節點沒有重寫,爺爺節點重寫了將會調用爺爺的,以此類推
但是如果所有的祖先線程組都沒有重寫呢?很顯然,所有的方法代碼都是上面這樣子的(上圖下半部分),將會遞歸到頂級線程組,然后不滿足parent,然后走到else,這中間什么有意義的事情都沒有做
在else中,會首先獲取應用默認的異常處理器,如果仍舊是沒有設置
不好意思,直接轉到system.err了
image_5c5bc46b_6c61

代碼示例

image_5c5bc46b_43cf
從上面的示例可以看得出來,盡管仍就出現了異常,我們能夠進行信息獲取與感知,不會直接宕掉了
如果先start,然后在設置異常處理器會發生什么?
image_5c5bc46b_6b91
可以看得到,線程仍舊是直接宕掉,異常處理器無效,所以setUncaughtExceptionHandler方法必須在start方法前調用!

總結

在Thread中的run方法,不能夠拋出異常,只能進行捕獲
  • 對於已檢查異常,必須捕獲
  • 對於未檢查異常,你也可以進行try catch,但是代碼始終包裹在try中,真的好嗎?
  • 還另外提供了異常處理器機制用於處理未檢查異常
有兩種異常處理器:
線程自身的處理器和全局的異常處理器
  1. 如果設置了異常處理器uncaughtExceptionHandler,那么將會使用這個
  2. 如果沒設置,將會在祖先線程組中查找第一個重寫了uncaughtException的線程組,然后調用他的uncaughtException方法
  3. 如果都沒有重寫,那么使用應用默認的全局異常處理器defaultUncaughtExceptionHandler
  4. 如果還是沒有設置,直接標准錯誤打印信息
如果想要設置自己的異常處理器,可以通過對應的setter方法進行設置,如果想要設置全局的可以調用靜態方法進行設置
異常處理器Thread.UncaughtExceptionHandler是一個函數式接口,所以后續,你可以使用Lambda表達式直接編寫,大大減少了工作量

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
  © 2014-2022 ITdaan.com