線程池異常信息統一處理,從線程異常信息丟失說起

異常信息作為我們平時定位問題的一大法寶,其重要性是不言而喻的。一段代碼出現了問題,如果它的異常信息被吃了,那麼一個本來很簡單的問題,定位起來也會變的很麻煩。

今天我們就從異常信息被吃掉,導致相應信息沒有輸出到日誌的問題說起。

一個簡單的例子

首先我們看看如下的代碼:

線程池異常信息統一處理,從線程異常信息丟失說起

這段代碼非常的簡單,創建一個線程池,實現一個Runnable,然後通過兩種不同方式來執行這個它。代碼很簡單,那麼它的運行結果是怎麼樣的呢?

下圖就是這段代碼的執行輸出:

線程池異常信息統一處理,從線程異常信息丟失說起

從結果可以發現,通過execute執行是可以正常輸出出錯堆棧的。而通過submit的方式執行異常信息不見了,這是為什麼呢?

submit方法執行時異常信息為什麼消失了

要想知道submit執行的線程異常信息為什麼消失了,就需要進一步分析它的源碼:

線程池異常信息統一處理,從線程異常信息丟失說起

從這小段源碼我們就可以知道submit和execute的區別了:submit會把參數中的Runnable通過RunnableFuture封裝一層後再調用execute。所以很明顯這個異常信息是被這個Future吃掉的了。

Future是如何吃掉異常信息的

進一步查看代碼,我們可以知道submit方法創建的Future實際類型是FutureTask,下面就是它最為核心的run方法源碼:

線程池異常信息統一處理,從線程異常信息丟失說起

從這段代碼可以發現,線程執行時拋出的任何異常信息(catch的是Throwable,所以連OOM都會被catch掉)都會被catch並通過setException保存為執行結果。

所以submit提交的線程任務執行的異常信息是不會直接拋出的,而是作為結果保存在返回的Future中,當我們調用Future.get方法的時候異常就會被拋出。由於run方法返回值類型是void的,所以Future中保存的一般都是異常信息。

如何拿到submit提交的線程任務的異常信息

上面分析了submit提交會生成Future,並且異常信息是作為結果保存在Future中的,所以想要拿到submit提交的線程任務的異常信息也就非常的簡單了。

線程池異常信息統一處理,從線程異常信息丟失說起

通過上面的分析,我們已經清楚了submit方式和execute方式的異常信息的處理方法。但是還有一個問題:這兩種提交方式的異常信息處理方式是不一樣的,那麼有沒有辦法實現統一呢?辦法肯定是有的!

通過統一Runnable父類實現異常信息的統一處理

我們可以實現一個統一的Runnable父類,重寫父類的run方法,定義一個running方法,然後run中通過try-catch包裹調用running方法,就能夠抓取全部的異常信息,從而不向上拋異常,並且能夠統一處理這些異常信息。實現代碼如下:

線程池異常信息統一處理,從線程異常信息丟失說起

通過自定義的線程池來對異常信息進行統一的處理。

通過查看線程池run方法相關的源碼我們可以發現,每次執行完線程任務後都會調用afterExecute方法,如下面的代碼所示:

線程池異常信息統一處理,從線程異常信息丟失說起

所以我們可以通過繼承線程池類來重寫這個afterExecute方法,從而達到異常信息的統一處理。實現如下所示:

線程池異常信息統一處理,從線程異常信息丟失說起


分享到:


相關文章: