#線程池中的一個線程異常了會被怎麼處理?java
估計不少人會是如下三點答案(me too):ui
1.拋異常出來並打印在控制檯上線程
2.其餘線程任務不受影響code
3.異常線程會被回收接口
可是這裏我先提早說一下以上三點不全對,下面咱們來具體分析一下。ci
#話很少說用代碼來證實get
熟悉Executors線程池(本文線程池都是指Executors)都知道 有兩種提交線程的方式execute和submit方式,下面將以這兩種提交方式來驗證。源碼
貼個代碼湊個數it
public static void main(String[] args) { ThreadPoolTaskExecutor executorService = buildThreadPoolTaskExecutor(); executorService.execute(() -> run("execute方法")); executorService.submit(() -> run("submit方法")); } private static void run(String name) { String printStr = "【thread-name:" + Thread.currentThread().getName() + ",執行方式:" + name+"】"; System.out.println(printStr); throw new RuntimeException(printStr + ",出現異常"); } private static ThreadPoolTaskExecutor buildThreadPoolTaskExecutor() { ThreadPoolTaskExecutor executorService = new ThreadPoolTaskExecutor(); executorService.setThreadNamePrefix("(小羅技術筆記)-"); executorService.setCorePoolSize(5); executorService.setMaxPoolSize(10); executorService.setQueueCapacity(100); executorService.setKeepAliveSeconds(10); executorService.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); executorService.initialize(); return executorService; }
觀看執行結果,誒好奇怪io
execute執行方式拋出異常顯示在控制檯了。
submit執行方式啥都沒有輸出。
衆所周知submit底層其實也是調用的execute,所以它也有異常只是處理方法不同,它們的區別是:
一、execute沒有返回值。能夠執行任務,但沒法判斷任務是否成功完成。——實現Runnable接口
二、submit返回一個future。能夠用這個future來判斷任務是否成功完成。——實現Callable接口
那怎麼拿到submit中的異常呢?仍是用代碼來講話
Future<?> result=executorService.submit(() -> run("submit方法")); try { result.get(); }catch (Exception e){ e.printStackTrace(); }
獲取了一下submit方法的返回結果,發現有異常了和execute同樣了,因此第一點拋異常出來並打印在控制檯上不是全對的!
到這估計你們和我同樣都有一個疑問了 ,爲啥execute直接拋出異常,submit沒有呢?
要知道這個答案就只能去翻源碼看了
在java.util.concurrent.ThreadPoolExecutor#runWorker中拋出了運行異常:
在java.lang.ThreadGroup#uncaughtException進行了異常處理:
uncaughtException是什麼,我也不知道,百度了一下說這個方法是JVM調用的,在線程中只須要指定咱們想要的處理方式便可
說道這裏你可能會吐槽說了這麼多submit到底爲啥沒有直接拋出異常,究竟是怎麼處理了,不要慌咱們再看源碼找答案
看submit源碼會發現,submit中傳進來的task會被封裝成一個FutureTask,而後再調用execute,最後返回FutureTask。
你會發現走的是execute方法,以下圖,會發現此時的task已是FutureTask,因此再去看一下FutureTask的run方法是咋寫的。
異常被存起來了....,再看源碼是怎麼實現的
返回參數裏面有一個狀態state翻源碼發如今report方法中同時用到outcom和state狀態判斷打個斷點發現還真是,當state爲3時拋出了異常。
同理在找尋一下report調用位置能夠很明顯發現是FutureTask中get方法調用了,結合上面能夠很明確了submit提交時異常被存儲在線程結果信息中,當調用get方法是判斷線程運行結果狀態,有異常就拋出存儲的異常信息,所以submit運行異常咱們只能用get方法來拿到。
#至於第二點我就很少說了,平時使用中就已經證實了!
#第三點線程出異常了不是被線程池回收嘛?
看源碼咱們知道線程運行最後總有一個processWorkerExit要執行,看看裏面的實現
很神奇先刪掉線程又再調用建立線程的方法,因此異常線程不是被回收,而是被刪除了再建立一個新的頂替了。
到此線程池中的線程異常了會被怎麼處理講完了,總結一下就是:
一、execute方法,能夠看異常輸出在控制檯,而submit在控制檯沒有直接輸出,必須調用Future.get()方法時,能夠捕獲到異常。
二、一個線程出現異常不會影響線程池裏面其餘線程的正常執行。
三、線程不是被回收而是線程池把這個線程移除掉,同時建立一個新的線程放到線程池中。
四、還有源碼是個好東西,答案都在裏面,就是太難看懂了