有的線程它死了,因而它變成一道面試題

有些線程它活着,但它躺在池中碌碌無爲;
有的線程它死了,因而它變成一道面試題。java

此次的文章,要從一次阿里巴巴的面試提及。web

我記得那天是週一,剛剛經歷過週末過的放鬆,幹勁十足的我正在鍵盤上瘋狂的輸出。這時,個人手機響了起來,拿起一看,是來自杭州的電話,心想此次是要給我推薦股票呢仍是要讓我貸款呢。我接起了電話,準備調戲一番。那邊響起一個聲音:"你好,請問是xxx嗎?這邊是杭州阿里巴巴,如今有時間進行電話面試嗎?"。說實在的,聽完這句話後,我感受我已經身在杭州,幹勁十足的在杭州的阿里的工位上"修福報"。可是我如今正在瘋狂輸出,沒有時間,因而我說:"很差意思,如今沒有時間,能夠約在今天晚上8點鐘嗎?".面試

晚上如約接到了電話。咱們直奔主題,在你來我往中進行了友好的技術交流。具體的面試過程就不詳述了,後面有機會整理一份面試分享。整個面試過程當中,有這麼一道題給我留下了深入的印象:編程

一個線程池中的線程異常了,那麼線程池會怎麼處理這個線程?多線程

須要說明一下,文中討論的線程池都是Executors線程池。編輯器

對於Executors線程池我能夠說是爛熟於心,由於工做中用的比較的多,閱讀過其源碼。也是我做爲面試官時必問的幾個範圍之一,好比如下問題:測試

瞭解JDK Executors線程池嗎?
知道JDK提供了哪些默認的實現嗎?
看過阿里巴巴java開發手冊嗎?知道爲啥不容許使用默認的實現嗎?
大家沒有用默認的吧?那來介紹一下大家自定義線程池的幾個經常使用參數唄?
你這個幾個參數的值是怎麼得來的呀?算出來的?怎麼算出來的?
線程池裏面的任務是IO密集型的仍是計算密集型的呢?
好,如今咱們有一個自定義線程池了,來講一下你這個線程池的工做流程唄?
那你這個線程池滿了怎麼辦呀?拒絕?咋拒絕?有哪些拒絕策略呢?
別緊張,隨便說兩個就行。
......
回到開始說的阿里巴巴java開發手冊不容許使用默認實現,你回答說可能會引發OOM,那咱們聊聊JVM吧
......spa

這一系列關於線程池的連環炮,就是我做爲面試官時必問的幾個問題。別問爲何,由於咱們的招聘JD上明確寫了:熟悉多線程編程。而這些問題,我以爲是熟悉多線程編程的基礎。這裏我也不解答了,這種文章網上仍是挺多的,能夠去了解一下。線程

這塊真的很重要,我也屢次給個人小夥伴強調:3d

來吧,一塊兒分析一波

好了如今回到阿里的面試官問個人這道面試題:

一個線程池中的線程異常了,那麼線程池會怎麼處理這個線程?

先說說我當時的回答,由於內心沒底,個人回答很猶豫也很爛!以下:

個人回答總結起來三句話:

1.拋出堆棧異常 ---這句話對了一半!
2.不影響其餘線程任務 ---這句話全對!
3.這個線程會被放回線程池---這句話全錯!

測試用例寫起來

拋出堆棧異常爲啥對了一半?

先讓程序跑起來,咱們用事實說話:

從執行結果咱們看出

當執行方式是execute時,能夠看到堆棧異常的輸出。

當執行方式是submit時,堆棧異常沒有輸出。

那麼咱們怎麼拿到submit執行方式的堆棧異常呢,看圖說話:

因此,如今知道爲何回答:拋出堆棧異常只對了一半吧。

execute方法執行時,會拋出(打印)堆棧異常。

submit方法執行時,返回結果封裝在future中,若是調用future.get()方法則必須進行異常捕獲,從而能夠拋出(打印)堆棧異常。

你覺得這一部分寫到這裏就完事了?那不行啊,你內心沒有一個疑問嗎?爲啥execute直接拋出異常,submit沒有直接拋出異常呢? 源碼之下無祕密:

當執行方式是executes時:

java.util.concurrent.ThreadPoolExecutor#runWorker中拋出了異常:

java.lang.ThreadGroup#uncaughtException進行了異常處理:

這個uncaughtException是何許人也,看java doc上咋說的:

這個方法是JVM調用的,咱們只須要指定咱們想要的處理方式便可。

那咱們怎麼指定呢:

當執行方式是submit時:

其本質也是調用了execute方法,因此它仍是回到java.util.concurrent.ThreadPoolExecutor#runWorker方法:

向前,繼續跟進去看看:

java.util.concurrent.FutureTask#setException幹啥了啊,瞅一眼:

深呼吸,整理好思路,咱們立刻走向最終的真相:

好了,第一個議題**【拋出堆棧異常爲啥對了一半?】**討論完畢。在源碼裏面走了一趟,如今咱們能夠給出這一部分的滿分答案了。

不影響其餘線程任務,回答正確

這一部分咱們直接上代碼,運行起來看結果吧:

代碼和運行結果是不會騙人的:

線程池中一個線程異常了後,不影響其餘線程任務

你們注意線程名稱這個細節:1,2,3,4,6。魔鬼都在細節裏啊,這個點我下面會講,先在這裏把問題拋出來:我就納悶了,怎麼沒有5啊?!

這個線程會被放回線程池爲啥全錯了?

咱們去源碼裏面尋找答案:

讓源碼給出答案:

5號線程去哪裏了?

new Worker()方法會告訴你:5去哪裏了。

再配上這張由我這個靈魂畫師親自操刀畫的圖,一塊兒食用,味道更佳:

如今咱們知道爲啥:我回答這個線程會被放回線程池爲啥全錯了吧。還附送你一個線程名稱變化的細節,不客氣,關注一下就好。

總結一下

當一個線程池裏面的線程異常後:

1.當執行方式是execute時,能夠看到堆棧異常的輸出。

2.當執行方式是submit時,堆棧異常沒有輸出。可是調用Future.get()方法時,能夠捕獲到異常。

3.不會影響線程池裏面其餘線程的正常執行。

4.線程池會把這個線程移除掉,並建立一個新的線程放到線程池中。

5.不要背答案,要理解,要深刻,上面說完後記得在問問面試官,須要我從源碼的角度講一講嗎?這逼裝的,禮貌而不失風度。

以上,我關於《一個線程池中的線程異常了,那麼線程池會怎麼處理這個線程?》這個問題的看法就表達完畢,僅表明我的觀點,歡迎有不一樣意見的小夥伴,一塊兒討論,一塊兒進步。

最後說一點

這篇文章是我上週五推完上一篇文章以後就在構思而且着手準備了。大部份內容都是思考於晚上睡覺前的半小時,寫於週末和工做日的早上早起的一小時。

其實想到寫什麼內容並不難,難的是你對內容的把控。關於技術性的語言,我是反覆推敲,查閱大量文章來進行證僞,總之慎言慎言再慎言,畢竟作技術,我認爲是一件很是嚴謹的事情,我經常想象本身就是在故宮修文物的工匠,在工匠精神的認知上,目前我可能和他們還差的有點遠,可是我時常以工匠精神要求本身。就像我在羣裏表達的:對於技術文章我儘可能保證周推,全力保證質量。

最後,再感嘆一次:

有些線程它活着,但它躺在池中碌碌無爲;
有些線程也活着,但它一刻不停忙到飛起;
有的線程它死了,被拋棄,被回收,
可是它無怨無悔,

由於它是死在執行任務的路上,
它憑藉本身最後的一聲吶喊
「爲了新兄弟,移除我吧!」
最後,變成一道面試題。

我還沒答上來。

歡迎關注公衆號【why技術】,堅持輸出原創。分享技術、品味生活,願你我共同進步。

相關文章
相關標籤/搜索