歡迎關注個人公眾號:石杉的架構筆記(ID:shishan100)
週一至週五早8點半!精品技術文章準時送上!
精品學習資料獲取通道,參見文末
目錄
(1)背景引入
(2)執行緒池是如何構造的?
(3)執行緒池的執行原理
(4)無界佇列引發的記憶體飆升
(1)背景引入
今天跟大家聊一個網際網路大廠的Java面試題:使用無界佇列的執行緒池會導致記憶體飆升嗎?
因為在面網際網路大廠的時候,一定會問併發,問併發的時候一定會問到執行緒池,問到執行緒池一定會問構造執行緒池的一些引數的含義。
然後,有一些面試官會就執行緒池的具體場景,問一些可能會遇到的問題。
所以,在這裡就可能有上述那樣一個面試中的問題,算是Java面試裡相對來說高階一點的。
我相信大家一定起碼知道執行緒池是個什麼東西的。簡單來說,就是維護一個池子,池子裡面放了很多的執行緒。
然後來一個任務,某個執行緒就獲取這個任務來執行,任務執行完之後執行緒是不會釋放掉的,而是停留線上程池裡繼續等待下一個任務。
這樣的一個好處是你沒必要自己手動頻繁的建立和銷燬執行緒,畢竟執行緒是較重的資源,頻繁的建立和銷燬對系統效能是沒好處的。
我們看看下面的圖,回顧一下執行緒池的含義。
(2)執行緒池是如何構造的?
那麼平時在Java裡寫程式碼的時候,大家記得不記得執行緒池是如何構造出來的呢?
是不是類似下面那樣的程式碼,比如說我們構造一個執行緒數量固定的一個執行緒池:
那麼Executors.newFixedThreadPool(10)內部到底又是如何構造出來執行緒池的呢?其實很簡單,翻開JDK原始碼就可以看到裡面的程式碼如下:
簡單來說,就是構造了一個ThreadPoolExecutor物件例項,你大致就認為他是一個執行緒池吧,傳入了一些引數,這些引數大致包含了:corePoolSize maximumPoolSize keepAliveTime workQueue
假如說我們構造執行緒池傳入的執行緒數量是10,那麼在這裡,corePoolSize和maximumSize都是10,keepAliveTime預設就是0,workQueue是一個無界的LinkedBlockingQueue。
接下來,我們具體來看看構造一個執行緒池傳入一些引數之後,具體這個執行緒池的執行原理是什麼。
(3)執行緒池的執行原理
簡單來說,剛開始的時候其實執行緒池裡是空的,就是一個執行緒都沒有的,如下圖所示。
接著如果你使用執行緒池提交一個任務進去,希望由執行緒池裡的一個執行緒來執行,如下程式碼所示,就是提交一個任務:
這個時候,執行緒池會先看一下,現在池子裡的執行緒數量有沒有有達到corePoolSize指定的數量。現線上程池裡的執行緒數量是0,然後corePoolSize是10,那麼肯定沒達到了,所以直接會線上程池裡建立一個執行緒出來然後執行這個任務,如下圖。
接著假如說,這個執行緒處理完一個任務了,那麼此時執行緒是不會被銷燬的,他會一直等待下一個提交過來的任務。
那麼,到底是怎麼等待的呢?
很簡單,執行緒池會搭配一個workQueue,比如這裡搭配的就是一個無界的LinkedBlockingQueue,幾乎可以無限量放入任務。
然後那個執行緒處理完一個任務之後,就會用阻塞的方式嘗試從任務佇列裡獲取任務,如果佇列是空的,他就會阻塞卡在那兒不動,直到有人放一個任務到佇列裡,他才會獲取到一個任務然後繼續執行,迴圈往復,如下圖。
接著再次提交任務,執行緒池一判斷髮現,誒?好像執行緒數量才只有1個,完全比corePoolSize(10個)要小,那麼繼續直接在池子裡建立一個執行緒,然後處理這個任務,處理完了繼續嘗試從workQueue裡阻塞式獲取任務。
一直重複上面的操作,直到執行緒池裡有10個執行緒了,達到了corePoolSize指定的數量,如下圖。
這個時候你如果再提交任務,他一下子發現,誒?不對啊,執行緒池裡已經有10個執行緒了,跟corePoolSize指定的執行緒數量一樣了。
那麼現在,我就不需要建立任何一個額外的執行緒了,現在你只要提交任務,全部直接入隊到workQueue裡就好。
此時執行緒池裡的執行緒都阻塞式在workQueue上等待獲取任務,有一個任務進來就會喚醒一個執行緒來處理這個任務,處理完了任務再次阻塞在workQueue上嘗試獲取下一個任務,如下圖所示這個意思。
這裡我們看到他用的是一個無界的LinkedBlockingQueue,但是假如說他用的是一個有界的佇列呢?
比如說限定好了佇列最多隻能放10個任務,那麼假如說,執行緒池裡的執行緒來不及處理任務了,然後佇列一下子放滿了10個任務。
此時就會出現任務入隊的失敗,因為佇列滿了,無法入隊。
然後就會嘗試再次線上程池裡建立執行緒,這個時候就會一直建立執行緒直到執行緒池裡的執行緒數量達到maximumPoolSize指定的數量為止。
雖然這裡fixed執行緒池預設corePoolSize和maximumPoolSize的數量都是一致的,但是可以假設此時maximumPoolSize的數量是20呢?
那麼就會繼續建立執行緒,直到執行緒數量達到20個,然後用額外建立的10個執行緒在佇列滿的情況下,繼續處理任務。
整個過程,如下圖所示:
接著萬一佇列滿了,然後執行緒池的執行緒數量達到了maximumPoolSize指定的數量了,你額外建立執行緒都無法建立了,此時會如何呢?
答案是:會reject掉,不讓你繼續提交任務了,此時預設的就是丟擲一個異常。
那麼,在上圖中額外建立出來的,超出corePoolSize的那些執行緒呢?
他們一旦建立出來之後,會發現執行緒池數量已經超過corePoolSize了,此時他們會嘗試等待workQueue裡的任務。
一旦超過keepAliveTime指定的時間,還獲取不到任務,比如keepAliveTime是60秒,那麼假如超過60秒獲取不到任務,他就會自動釋放掉了,這個執行緒就銷燬了。
整個過程,如下圖所示。
(4)無界佇列引發的記憶體飆升
明白了執行緒池的執行原理了,這個面試題就好解答了。
我們以最常用的fixed執行緒池舉例,他的執行緒池數量是固定的,因為他用的是近乎於無界的LinkedBlockingQueue,幾乎可以無限制的放入任務到佇列裡。
所以只要執行緒池裡的執行緒數量達到了corePoolSize指定的數量之後,接下來就維持這個固定數量的執行緒了。
然後,所有任務都會入隊到workQueue裡去,執行緒從workQueue獲取任務來處理。
這個佇列幾乎永遠不會滿,當然這是幾乎,因為LinkedBlockingQueue預設的最大任務數量是Integer.MAX_VALUE,非常大,近乎於可以理解為無限吧。
只要佇列不滿,就跟maximumPoolSize、keepAliveTime這些沒關係了,因為不會建立超過corePoolSize數量的執行緒的。
同樣,給大家來一張圖,我們來看看:
那麼此時萬一每個執行緒獲取到一個任務之後,他處理的時間特別特別的長,長到了令人髮指的地步。比如處理一個任務要幾個小時,此時會如何?
當然會出現workQueue裡不斷的積壓越來越多得任務,不停的增加。
這個過程中會導致機器的記憶體使用不停的飆升,最後也許極端情況下就導致JVM OOM了,系統就掛掉了。
所以這就是這個面試題背後你要知道的執行緒池的執行原理,以及可能遇到的一些問題,大家要做到心裡有數。
END
掃描下方二維碼,備註:“資料”,獲取更多“祕製” 精品學習資料
如有收穫,請幫忙轉發,您的鼓勵是作者最大的動力,謝謝!
一大波微服務、分散式、高併發、高可用的原創系列文章正在路上
歡迎掃描下方二維碼,持續關注:
石杉的架構筆記(id:shishan100)
十餘年BAT架構經驗傾囊相授
推薦閱讀:
2、【雙11狂歡的背後】微服務註冊中心如何承載大型系統的千萬級訪問?
3、【效能優化之道】每秒上萬併發下的Spring Cloud引數優化實戰
6、大規模叢集下Hadoop NameNode如何承載每秒上千次的高併發訪問
7、【效能優化的祕密】Hadoop如何將TB級大檔案的上傳效能優化上百倍
9、【坑爹呀!】最終一致性分散式事務如何保障實際生產中99.99%高可用?
11、【眼前一亮!】看Hadoop底層演算法如何優雅的將大規模叢集效能提升10倍以上?
16、億級流量系統架構之如何設計全鏈路99.99%高可用架構
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、億級流量系統架構之如何在上萬併發場景下設計可擴充套件架構(下)?
37、億級流量系統架構之如何保證百億流量下的資料一致性(上)
38、億級流量系統架構之如何保證百億流量下的資料一致性(中)?
39、億級流量系統架構之如何保證百億流量下的資料一致性(下)?
40、網際網路面試必殺:如何保證訊息中介軟體全鏈路資料100%不丟失(1)
41、網際網路面試必殺:如何保證訊息中介軟體全鏈路資料100%不丟失(2)
42、面試大殺器:訊息中介軟體如何實現消費吞吐量的百倍優化?
43、高併發場景下,如何保證生產者投遞到訊息中介軟體的訊息不丟失?
45、從團隊自研的百萬併發中介軟體系統的核心設計看Java併發效能優化
46、【非廣告,純乾貨】英語差的程式設計師如何才能無障礙閱讀官方文件?
47、如果20萬使用者同時訪問一個熱點快取,如何優化你的快取架構?
48、【非廣告,純乾貨】中小公司的Java工程師應該如何逆襲衝進BAT?
作者:石杉的架構筆記 連結:juejin.im/post/5c263a… 來源:掘金 著作權歸作者所有,轉載請聯絡作者獲得授權!