網際網路大廠Java面試題:使用無界佇列的執行緒池會導致記憶體飆升嗎?【石杉的架構筆記】

石杉的架構筆記發表於2019-03-03

歡迎關注個人公眾號:石杉的架構筆記(ID:shishan100)

週一至週五早8點半!精品技術文章準時送上!

精品學習資料獲取通道,參見文末

目錄

(1)背景引入

(2)執行緒池是如何構造的?

(3)執行緒池的執行原理

(4)無界佇列引發的記憶體飆升

(1)背景引入

今天跟大家聊一個網際網路大廠的Java面試題:使用無界佇列的執行緒池會導致記憶體飆升嗎?

因為在面網際網路大廠的時候,一定會問併發,問併發的時候一定會問到執行緒池,問到執行緒池一定會問構造執行緒池的一些引數的含義。

然後,有一些面試官會就執行緒池的具體場景,問一些可能會遇到的問題。

所以,在這裡就可能有上述那樣一個面試中的問題,算是Java面試裡相對來說高階一點的。

我相信大家一定起碼知道執行緒池是個什麼東西的。簡單來說,就是維護一個池子,池子裡面放了很多的執行緒。

然後來一個任務,某個執行緒就獲取這個任務來執行,任務執行完之後執行緒是不會釋放掉的,而是停留線上程池裡繼續等待下一個任務。

這樣的一個好處是你沒必要自己手動頻繁的建立和銷燬執行緒,畢竟執行緒是較重的資源,頻繁的建立和銷燬對系統效能是沒好處的。

我們看看下面的圖,回顧一下執行緒池的含義。

網際網路大廠Java面試題:使用無界佇列的執行緒池會導致記憶體飆升嗎?【石杉的架構筆記】

(2)執行緒池是如何構造的?

那麼平時在Java裡寫程式碼的時候,大家記得不記得執行緒池是如何構造出來的呢?

是不是類似下面那樣的程式碼,比如說我們構造一個執行緒數量固定的一個執行緒池:

網際網路大廠Java面試題:使用無界佇列的執行緒池會導致記憶體飆升嗎?【石杉的架構筆記】
那麼Executors.newFixedThreadPool(10)內部到底又是如何構造出來執行緒池的呢?

其實很簡單,翻開JDK原始碼就可以看到裡面的程式碼如下:

網際網路大廠Java面試題:使用無界佇列的執行緒池會導致記憶體飆升嗎?【石杉的架構筆記】
簡單來說,就是構造了一個ThreadPoolExecutor物件例項,你大致就認為他是一個執行緒池吧,傳入了一些引數,這些引數大致包含了:

corePoolSize maximumPoolSize keepAliveTime workQueue

假如說我們構造執行緒池傳入的執行緒數量是10,那麼在這裡,corePoolSize和maximumSize都是10,keepAliveTime預設就是0,workQueue是一個無界的LinkedBlockingQueue。

接下來,我們具體來看看構造一個執行緒池傳入一些引數之後,具體這個執行緒池的執行原理是什麼。

(3)執行緒池的執行原理

簡單來說,剛開始的時候其實執行緒池裡是空的,就是一個執行緒都沒有的,如下圖所示。

網際網路大廠Java面試題:使用無界佇列的執行緒池會導致記憶體飆升嗎?【石杉的架構筆記】

接著如果你使用執行緒池提交一個任務進去,希望由執行緒池裡的一個執行緒來執行,如下程式碼所示,就是提交一個任務:

網際網路大廠Java面試題:使用無界佇列的執行緒池會導致記憶體飆升嗎?【石杉的架構筆記】
這個時候,執行緒池會先看一下,現在池子裡的執行緒數量有沒有有達到corePoolSize指定的數量。

現線上程池裡的執行緒數量是0,然後corePoolSize是10,那麼肯定沒達到了,所以直接會線上程池裡建立一個執行緒出來然後執行這個任務,如下圖。

網際網路大廠Java面試題:使用無界佇列的執行緒池會導致記憶體飆升嗎?【石杉的架構筆記】

接著假如說,這個執行緒處理完一個任務了,那麼此時執行緒是不會被銷燬的,他會一直等待下一個提交過來的任務。

那麼,到底是怎麼等待的呢?

很簡單,執行緒池會搭配一個workQueue,比如這裡搭配的就是一個無界的LinkedBlockingQueue,幾乎可以無限量放入任務。

然後那個執行緒處理完一個任務之後,就會用阻塞的方式嘗試從任務佇列裡獲取任務,如果佇列是空的,他就會阻塞卡在那兒不動,直到有人放一個任務到佇列裡,他才會獲取到一個任務然後繼續執行,迴圈往復,如下圖。

網際網路大廠Java面試題:使用無界佇列的執行緒池會導致記憶體飆升嗎?【石杉的架構筆記】

接著再次提交任務,執行緒池一判斷髮現,誒?好像執行緒數量才只有1個,完全比corePoolSize(10個)要小,那麼繼續直接在池子裡建立一個執行緒,然後處理這個任務,處理完了繼續嘗試從workQueue裡阻塞式獲取任務。

一直重複上面的操作,直到執行緒池裡有10個執行緒了,達到了corePoolSize指定的數量,如下圖。

網際網路大廠Java面試題:使用無界佇列的執行緒池會導致記憶體飆升嗎?【石杉的架構筆記】

這個時候你如果再提交任務,他一下子發現,誒?不對啊,執行緒池裡已經有10個執行緒了,跟corePoolSize指定的執行緒數量一樣了。

那麼現在,我就不需要建立任何一個額外的執行緒了,現在你只要提交任務,全部直接入隊到workQueue裡就好。

此時執行緒池裡的執行緒都阻塞式在workQueue上等待獲取任務,有一個任務進來就會喚醒一個執行緒來處理這個任務,處理完了任務再次阻塞在workQueue上嘗試獲取下一個任務,如下圖所示這個意思。

網際網路大廠Java面試題:使用無界佇列的執行緒池會導致記憶體飆升嗎?【石杉的架構筆記】

這裡我們看到他用的是一個無界的LinkedBlockingQueue,但是假如說他用的是一個有界的佇列呢?

比如說限定好了佇列最多隻能放10個任務,那麼假如說,執行緒池裡的執行緒來不及處理任務了,然後佇列一下子放滿了10個任務。

此時就會出現任務入隊的失敗,因為佇列滿了,無法入隊。

然後就會嘗試再次線上程池裡建立執行緒,這個時候就會一直建立執行緒直到執行緒池裡的執行緒數量達到maximumPoolSize指定的數量為止。

雖然這裡fixed執行緒池預設corePoolSize和maximumPoolSize的數量都是一致的,但是可以假設此時maximumPoolSize的數量是20呢?

那麼就會繼續建立執行緒,直到執行緒數量達到20個,然後用額外建立的10個執行緒在佇列滿的情況下,繼續處理任務。

整個過程,如下圖所示:

網際網路大廠Java面試題:使用無界佇列的執行緒池會導致記憶體飆升嗎?【石杉的架構筆記】

接著萬一佇列滿了,然後執行緒池的執行緒數量達到了maximumPoolSize指定的數量了,你額外建立執行緒都無法建立了,此時會如何呢?

答案是:會reject掉,不讓你繼續提交任務了,此時預設的就是丟擲一個異常。

那麼,在上圖中額外建立出來的,超出corePoolSize的那些執行緒呢?

他們一旦建立出來之後,會發現執行緒池數量已經超過corePoolSize了,此時他們會嘗試等待workQueue裡的任務。

一旦超過keepAliveTime指定的時間,還獲取不到任務,比如keepAliveTime是60秒,那麼假如超過60秒獲取不到任務,他就會自動釋放掉了,這個執行緒就銷燬了。

整個過程,如下圖所示。

網際網路大廠Java面試題:使用無界佇列的執行緒池會導致記憶體飆升嗎?【石杉的架構筆記】

(4)無界佇列引發的記憶體飆升

明白了執行緒池的執行原理了,這個面試題就好解答了。

我們以最常用的fixed執行緒池舉例,他的執行緒池數量是固定的,因為他用的是近乎於無界的LinkedBlockingQueue,幾乎可以無限制的放入任務到佇列裡。

所以只要執行緒池裡的執行緒數量達到了corePoolSize指定的數量之後,接下來就維持這個固定數量的執行緒了。

然後,所有任務都會入隊到workQueue裡去,執行緒從workQueue獲取任務來處理。

這個佇列幾乎永遠不會滿,當然這是幾乎,因為LinkedBlockingQueue預設的最大任務數量是Integer.MAX_VALUE,非常大,近乎於可以理解為無限吧。

只要佇列不滿,就跟maximumPoolSize、keepAliveTime這些沒關係了,因為不會建立超過corePoolSize數量的執行緒的。

同樣,給大家來一張圖,我們來看看:

網際網路大廠Java面試題:使用無界佇列的執行緒池會導致記憶體飆升嗎?【石杉的架構筆記】

那麼此時萬一每個執行緒獲取到一個任務之後,他處理的時間特別特別的長,長到了令人髮指的地步。比如處理一個任務要幾個小時,此時會如何?

當然會出現workQueue裡不斷的積壓越來越多得任務,不停的增加。

這個過程中會導致機器的記憶體使用不停的飆升,最後也許極端情況下就導致JVM OOM了,系統就掛掉了。

所以這就是這個面試題背後你要知道的執行緒池的執行原理,以及可能遇到的一些問題,大家要做到心裡有數。

END

掃描下方二維碼,備註:“資料”,獲取更多“祕製” 精品學習資料

網際網路大廠Java面試題:使用無界佇列的執行緒池會導致記憶體飆升嗎?【石杉的架構筆記】

如有收穫,請幫忙轉發,您的鼓勵是作者最大的動力,謝謝!

一大波微服務、分散式、高併發、高可用的原創系列文章正在路上

歡迎掃描下方二維碼,持續關注:

網際網路大廠Java面試題:使用無界佇列的執行緒池會導致記憶體飆升嗎?【石杉的架構筆記】

石杉的架構筆記(id:shishan100)

十餘年BAT架構經驗傾囊相授

推薦閱讀:

1、拜託!面試請不要再問我Spring Cloud底層原理

2、【雙11狂歡的背後】微服務註冊中心如何承載大型系統的千萬級訪問?

3、【效能優化之道】每秒上萬併發下的Spring Cloud引數優化實戰

4、微服務架構如何保障雙11狂歡下的99.99%高可用

5、兄弟,用大白話告訴你小白都能聽懂的Hadoop架構原理

6、大規模叢集下Hadoop NameNode如何承載每秒上千次的高併發訪問

7、【效能優化的祕密】Hadoop如何將TB級大檔案的上傳效能優化上百倍

8、拜託,面試請不要再問我TCC分散式事務的實現原理!

9、【坑爹呀!】最終一致性分散式事務如何保障實際生產中99.99%高可用?

10、拜託,面試請不要再問我Redis分散式鎖的實現原理!

11、【眼前一亮!】看Hadoop底層演算法如何優雅的將大規模叢集效能提升10倍以上?

12、億級流量系統架構之如何支撐百億級資料的儲存與計算

13、億級流量系統架構之如何設計高容錯分散式計算系統

14、億級流量系統架構之如何設計承載百億流量的高效能架構

15、億級流量系統架構之如何設計每秒十萬查詢的高併發架構

16、億級流量系統架構之如何設計全鏈路99.99%高可用架構

17、七張圖徹底講清楚ZooKeeper分散式鎖的實現原理

18、大白話聊聊Java併發面試問題之volatile到底是什麼?

19、大白話聊聊Java併發面試問題之Java 8如何優化CAS效能?

20、大白話聊聊Java併發面試問題之談談你對AQS的理解?

21、大白話聊聊Java併發面試問題之公平鎖與非公平鎖是啥?

22、大白話聊聊Java併發面試問題之微服務註冊中心的讀寫鎖優化

23、網際網路公司的面試官是如何360°無死角考察候選人的?(上篇)

24、網際網路公司面試官是如何360°無死角考察候選人的?(下篇)

25、Java進階面試系列之一:哥們,你們的系統架構中為什麼要引入訊息中介軟體?

26、【Java進階面試系列之二】:哥們,那你說說系統架構引入訊息中介軟體有什麼缺點?

27、【行走的Offer收割機】記一位朋友斬獲BAT技術專家Offer的面試經歷

28、【Java進階面試系列之三】哥們,訊息中介軟體在你們專案裡是如何落地的?

29、【Java進階面試系列之四】扎心!線上服務當機時,如何保證資料100%不丟失?

30、一次JVM FullGC的背後,竟隱藏著驚心動魄的線上生產事故!

31、【高併發優化實踐】10倍請求壓力來襲,你的系統會被擊垮嗎?

32、【Java進階面試系列之五】訊息中介軟體叢集崩潰,如何保證百萬生產資料不丟失?

33、億級流量系統架構之如何在上萬併發場景下設計可擴充套件架構(上)?

34、億級流量系統架構之如何在上萬併發場景下設計可擴充套件架構(中)?

35、億級流量系統架構之如何在上萬併發場景下設計可擴充套件架構(下)?

36、億級流量架構第二彈:你的系統真的無懈可擊嗎?

37、億級流量系統架構之如何保證百億流量下的資料一致性(上)

38、億級流量系統架構之如何保證百億流量下的資料一致性(中)?

39、億級流量系統架構之如何保證百億流量下的資料一致性(下)?

40、網際網路面試必殺:如何保證訊息中介軟體全鏈路資料100%不丟失(1)

41、網際網路面試必殺:如何保證訊息中介軟體全鏈路資料100%不丟失(2

42、面試大殺器:訊息中介軟體如何實現消費吞吐量的百倍優化?

43、高併發場景下,如何保證生產者投遞到訊息中介軟體的訊息不丟失?

44、兄弟,用大白話給你講小白都能看懂的分散式系統容錯架構

45、從團隊自研的百萬併發中介軟體系統的核心設計看Java併發效能優化

46、【非廣告,純乾貨】英語差的程式設計師如何才能無障礙閱讀官方文件?

47、如果20萬使用者同時訪問一個熱點快取,如何優化你的快取架構?

48、【非廣告,純乾貨】中小公司的Java工程師應該如何逆襲衝進BAT?

49、拜託,面試請不要再問我分散式搜尋引擎的架構原理!

作者:石杉的架構筆記 連結:juejin.im/post/5c263a… 來源:掘金 著作權歸作者所有,轉載請聯絡作者獲得授權!

相關文章