有些線程它活著,但它躺在池中碌碌無爲;
有的線程它死了,于是它變成一道面試題。
這次的文章,要從一次阿裏的面試說起。
我記得那天是周一,剛剛經曆過周末過的放松,幹勁十足的我正在鍵盤上瘋狂的輸出。這時,我的手機響了起來,拿起一看,是來自杭州的電話,心想這次是要給我推薦股票呢還是要讓我貸款呢。我接起了電話,准備“調戲一番”。那邊響起一個聲音:”你好,請問是xxx嗎?這邊是杭州阿裏巴巴,現在有時間進行電話面試嗎?”。說實在的,聽完這句話後,我感覺我已經身在杭州,幹勁十足的在杭州的阿裏的工位上”修福報”。但是我現在正在瘋狂輸出,沒有時間,于是我說:”不好意思,現在沒有時間,可以約在今天晚上8點鍾嗎?”.
晚上如約接到了電話。我們直奔主題,在你來我往中進行了友好的技術交流。具體的面試過程就不詳述了,後面有機會整理一份面試分享。整個面試過程中,有這麽一道題給我留下了深刻的印象:
一個線程池中的線程異常了,那麽線程池會怎麽處理這個線程?
需要說明一下,文中討論的線程池都是Executors線程池。
對于Executors線程池我可以說是爛熟于心,因爲工作中用的比較的多,閱讀過其源碼。也是我作爲面試官時必問的幾個範圍之一,比如以下問題:
了解JDK Executors線程池嗎?知道JDK提供了哪些默認的實現嗎?看過阿裏巴巴java開發手冊嗎?知道爲啥不允許使用默認的實現嗎?你們沒有用默認的吧?那來介紹一下你們自定義線程池的幾個常用參數呗?你這個幾個參數的值是怎麽得來的呀?算出來的?怎麽算出來的?線程池裏面的任務是IO密集型的還是計算密集型的呢?好,現在我們有一個自定義線程池了,來說一下你這個線程池的工作流程呗?那你這個線程池滿了怎麽辦呀?拒絕?咋拒絕?有哪些拒絕策略呢?別緊張,隨便說兩個就行。……回到開始說的阿裏巴巴java開發手冊不允許使用默認實現,你回答說可能會引起OOM,那我們聊聊JVM吧……
來吧,一起分析一波
好了現在回到阿裏的面試官問我的這道面試題:
一個線程池中的線程異常了,那麽線程池會怎麽處理這個線程?先說說我當時的回答,因爲心裏沒底,我的回答很猶豫也很爛!如下:
從執行結果我們看出
當執行方式是execute時,可以看到堆棧異常的輸出。當執行方式是submit時,堆棧異常沒有輸出。那麽我們怎麽拿到submit執行方式的堆棧異常呢,看圖說話:
所以,現在知道爲什麽回答:抛出堆棧異常只對了一半吧。execute方法執行時,會抛出(打印)堆棧異常。submit方法執行時,返回結果封裝在future中,如果調用future.get()方法則必須進行異常捕獲,從而可以抛出(打印)堆棧異常。你以爲這一部分寫到這裏就完事了?那不行啊,你心裏沒有一個疑問嗎?爲啥execute直接抛出異常,submit沒有直接抛出異常呢?
源碼之下無秘密:當執行方式是executes時:在java.util.concurrent.ThreadPoolExecutor#runWorker中抛出了異常:
這個uncaughtException是何許人也,看java doc上咋說的:
其本質也是調用了execute方法,所以它還是回到java.util.concurrent.ThreadPoolExecutor#runWorker方法:
java.util.concurrent.FutureTask#setException幹啥了啊,瞅一眼:
好了,第一個議題【抛出堆棧異常爲啥對了一半?】討論完畢。在源碼裏面走了一趟,現在我們可以給出這一部分的滿分答案了。
不影響其他線程任務,回答正確
這一部分我們直接上代碼,運行起來看結果吧:
讓源碼給出答案:
new Worker()方法會告訴你:5去哪裏了。
現在知道爲啥:我回答這個線程會被放回線程池爲啥全錯了吧。還附帶送你一個線程名稱變化的細節,不客氣。
總結一下
當一個線程池裏面的線程異常後:當執行方式是execute時,可以看到堆棧異常的輸出。當執行方式是submit時,堆棧異常沒有輸出。但是調用Future.get()方法時,可以捕獲到異常。不會影響線程池裏面其他線程的正常執行。線程池會把這個線程移除掉,並創建一個新的線程放到線程池中。不要背答案,要理解,要深入,上面說完後記得在問問面試官,需要我從源碼的角度講一講嗎?這逼裝的,禮貌而不失風度。
來源:https://www.cnblogs.com/thisiswhy/p/12221335.html