點擊關注公眾號,實用技術文章及時了解
看源碼可以知道,ThreadPoolExecutor中的任務都是在runWorker中執行的
![](https://imageproxy.pixnet.cc/imgproxy?url=https://drbanana.ml/img/68747470733a2f2f6d6d62697a2e717069632e636e2f6d6d62697a5f706e672f65515079426666596275664376626f5335383743757758444a426855517556316e4c32347730704541635a523362736963724a55567256445157634655776d5a47437457515851564a44594d3653377a7050364d4241772f3634303f77785f666d743d706e67.webp)
通過源碼可以看到
看一下線程退出的邏輯
![](https://imageproxy.pixnet.cc/imgproxy?url=https://drbanana.ml/img/68747470733a2f2f6d6d62697a2e717069632e636e2f6d6d62697a5f706e672f65515079426666596275664376626f5335383743757758444a42685551755631717543536c437770664f55337a587a787862346f344661455776584d645369623670327854754f71775161696375736672696335704e4843672f3634303f77785f666d743d706e67.webp)
ThreadPoolExecutor.execute 如果用戶任務拋出了異常,在線程池運行的狀態下,會重新創建一個worker線程。
這裡就可能存在一個風險,即如果用戶任務大量的拋出異常,可能會導出線程資源頻繁的銷毀、創建。
因此,需要用戶任務應當主動對異常進行處理,而不是消極的拋給線程池。
ThreadPoolExecutor.submit源碼分析通過 ThreadPoolExecutor.submit 提交的用戶任務,會包裝成一個FutureTask,返回一個Future對象。因此異常處理的邏輯和ThreadPoolExecutor.execute有些差別
看一個FutureTask.run方法
![](https://imageproxy.pixnet.cc/imgproxy?url=https://drbanana.ml/img/68747470733a2f2f6d6d62697a2e717069632e636e2f6d6d62697a5f706e672f65515079426666596275664376626f5335383743757758444a42685551755631664e4f773434524546515575514230366f586b39776c586c72664f63426f416233763747696234355a5161326961535949475041794d74412f3634303f77785f666d743d706e67.webp)
從源碼可以看到,FutureTask執行用戶任務,如果異常,不會對外拋出,僅是記錄
但是在調用Future.get時,會拋出異常,但此時的線程不是線程池的線程了,而是用戶線程,因此對線程池是友好的。
總結ThreadPoolExecutor.submit 提交的用戶任務,會包裝成一個FutureTask,FutureTask執行用戶任務,如果異常,不會對外拋出,僅是記錄,但是在調用Future.get時,會拋出異常。
異常是在用戶線程中拋出的,因此不影響線程池中的線程。
ScheduledThreadPoolExecutor.schedule源碼分析ScheduledThreadPoolExecutor.schedule會將用戶任務包裝為ScheduledFutureTask,ScheduledFutureTask是FutureTask的子類,看下ScheduledFutureTask的執行邏輯
![](https://imageproxy.pixnet.cc/imgproxy?url=https://drbanana.ml/img/68747470733a2f2f6d6d62697a2e717069632e636e2f6d6d62697a5f706e672f65515079426666596275664376626f5335383743757758444a426855517556316963634c387a597133573541447269634c71435164754333586645337a44724b74545a364e623477626a58355570496c646464477a6663412f3634303f77785f666d743d706e67.webp)
ScheduledFutureTask是FutureTask的子類,所以有異常時,也不是拋出,而是記錄
ScheduledThreadPoolExecutor.schedule提交的用戶任務,如果出現異常,是不會拋出的,也不會打印。
對於非周期性任務和周期性任務,執行異常,都沒有辦法感知(不拋出、看不到)。
對於周期性任務,任何一次調度時,任務出現異常,都會導致後續無法調度。
因此,在使用這個線程池時,我們應當保證用戶任務不會拋出異常到執行線程,避免任務調度失效,和異常排查困難等問題。
思考:ThreadPoolExecutor.execute發生異常時為什麼要退出ThreadPoolExecutor.execute出現用戶任務異常時,為什麼要退出當前線程,再重新創建一個線程呢?
我思考了半天,覺得原因之一可能是:
ThreadPoolExecutor的可以指定線程工廠,如果我的線程工廠是這樣的
ThreadFactorythreadFactory=newThreadFactory(){@OverridepublicThreadnewThread(Runnabler){Threadthread=newThread(r);thread.setUncaughtExceptionHandler(newThread.UncaughtExceptionHandler(){@OverridepublicvoiduncaughtException(Threadt,Throwablee){//處理異常的邏輯}});returnthread;}};即在ThreadFactory創建線程時,指定了對未捕獲的異常的處理器。
這種情況下,如果線程池不把發生異常的線程退出,可能會導致異常沒有走到用戶期望的邏輯上,因此需要將發生異常的線程退出,然後JVM調用UncaughtExceptionHandler。
推薦
Java面試題寶典
技術內卷群,一起來學習!!
PS:因為公眾號平台更改了推送規則,如果不想錯過內容,記得讀完點一下「在看」,加個「星標」,這樣每次新文章推送才會第一時間出現在你的訂閱列表里。點「在看」支持我們吧!