在Thread中有異常處理器相關的方法
在ThreadGroup中也有相關的異常處理方法
示例
未檢查異常
對于未檢查異常,将會直接宕掉,主線程則繼續運作,程式會繼續運作
在主線程中能不能捕獲呢?
我們簡單粗暴一點,直接全部包到try catch中
你會發現,然而并沒有什麼卵用,主線程中的try catch并不會得到什麼資訊,跟原來的結果還是一樣的,線程直接宕掉
已檢查異常
對于已檢查的異常,run方法本身是不支援抛出的,上面代碼中,想要throws,IDE提示異常,從run方法可以看得出來
run方法本身是不支援throws的(簽名中沒有throws)
是以怎麼辦?
既然是已檢查異常,肯定是要處理的,既然不能丢出去,就隻有一個辦法了,那就是自己捕獲,放置在try catch中
小結
在run方法中是不能夠抛出異常的,如果是已檢查的異常,那麼必須進行try catch
對于未檢查的異常,如果沒有進行處理,一旦抛出線程将會宕掉,而且在主線程中并不能捕獲到這個異常
難道對于未檢查的異常也都是try catch嗎?(當然,這是一種方式)
還有沒有其他解決方案?
異常處理器
在Java線程的run方法中,對于未檢查異常,借助于異常處理器進行處理的
字面意思,直接了解為處理異常的方法,那麼如何配置這個處理異常的方法呢?如何設定,又是如何調用?
UncaughtExceptionHandler,是Thread的内部接口(1.8中已經設定為函數式接口)
Thread内部有兩個變量,用于記錄異常處理器
對于兩個set方法,沒有什麼特别的,主要就是設定這兩個内部變量
對于getUncaughtExceptionHandler方法,如果目前非空,那麼傳回目前,否則,将傳回目前線程組,很顯然,ThreadGroup實作了Thread.UncaughtExceptionHandler
對于getDefaultUncaughtExceptionHandler,這是簡單的傳回内部變量
此時我們大緻了解到了這幾個方法,内部有兩個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調用
是以關鍵點在于getUncaughtExceptionHandler傳回什麼異常處理器,我們再回過頭來看下源代碼
如果已經設定,那麼将會直接傳回;
如果沒有設定,将會傳回目前線程組(前面說了ThreadGroup實作了Thread.UncaughtExceptionHandler)
當調用ThreadGroup的uncaughtException方法時,如上圖下半部分
如果他的父線程組重寫了uncaughtException方法,那麼将會調用他的父線程組的方法,如果父親節點沒有重寫,爺爺節點重寫了将會調用爺爺的,以此類推
但是如果所有的祖先線程組都沒有重寫呢?很顯然,所有的方法代碼都是上面這樣子的(上圖下半部分),将會遞歸到***線程組,然後不滿足parent,然後走到else,這中間什麼有意義的事情都沒有做
在else中,會首先擷取應用預設的異常處理器,如果仍舊是沒有設定
不好意思,直接轉到system.err了
代碼示例
從上面的示例可以看得出來,盡管仍就出現了異常,我們能夠進行資訊擷取與感覺,不會直接宕掉了
如果先start,然後在設定異常處理器會發生什麼?
可以看得到,線程仍舊是直接宕掉,異常處理器無效,是以setUncaughtExceptionHandler方法必須在start方法前調用!
總結
在Thread中的run方法,不能夠抛出異常,隻能進行捕獲
- 對于已檢查異常,必須捕獲
- 對于未檢查異常,你也可以進行try catch,但是代碼始終包裹在try中,真的好嗎?
- 還另外提供了異常處理器機制用于處理未檢查異常
有兩種異常處理器:
線程自身的處理器和全局的異常處理器
- 如果設定了異常處理器uncaughtExceptionHandler,那麼将會使用這個
- 如果沒設定,将會在祖先線程組中查找第一個重寫了uncaughtException的線程組,然後調用他的uncaughtException方法
- 如果都沒有重寫,那麼使用應用預設的全局異常處理器defaultUncaughtExceptionHandler
- 如果還是沒有設定,直接标準錯誤列印資訊
如果想要設定自己的異常處理器,可以通過對應的setter方法進行設定,如果想要設定全局的可以調用靜态方法進行設定
異常處理器Thread.UncaughtExceptionHandler是一個函數式接口,是以後續,你可以使用Lambda表達式直接編寫,大大減少了工作量
原文位址:異常處理器詳解 Java多線程異常處理機制 多線程中篇(四)