您好,登錄后才能下訂單哦!
本篇內(nèi)容主要講解“使用Java無界隊(duì)列的線程池會(huì)怎么樣”,感興趣的朋友不妨來看看。本文介紹的方法操作簡(jiǎn)單快捷,實(shí)用性強(qiáng)。下面就讓小編來帶大家學(xué)習(xí)“使用Java無界隊(duì)列的線程池會(huì)怎么樣”吧!
今天跟大家聊一個(gè)互聯(lián)網(wǎng)大廠的Java面試題:使用無界隊(duì)列的線程池會(huì)導(dǎo)致內(nèi)存飆升嗎?
因?yàn)樵诿婊ヂ?lián)網(wǎng)大廠的時(shí)候,一定會(huì)問并發(fā),問并發(fā)的時(shí)候一定會(huì)問到線程池,問到線程池一定會(huì)問構(gòu)造線程池的一些參數(shù)的含義。
然后,有一些面試官會(huì)就線程池的具體場(chǎng)景,問一些可能會(huì)遇到的問題。
所以,在這里就可能有上述那樣一個(gè)面試中的問題,算是Java面試?yán)锵鄬?duì)來說高階一點(diǎn)的。
我相信大家一定起碼知道線程池是個(gè)什么東西的。簡(jiǎn)單來說,就是維護(hù)一個(gè)池子,池子里面放了很多的線程。
然后來一個(gè)任務(wù),某個(gè)線程就獲取這個(gè)任務(wù)來執(zhí)行,任務(wù)執(zhí)行完之后線程是不會(huì)釋放掉的,而是停留在線程池里繼續(xù)等待下一個(gè)任務(wù)。
這樣的一個(gè)好處是你沒必要自己手動(dòng)頻繁的創(chuàng)建和銷毀線程,畢竟線程是較重的資源,頻繁的創(chuàng)建和銷毀對(duì)系統(tǒng)性能是沒好處的。
我們看看下面的圖,回顧一下線程池的含義。
那么平時(shí)在Java里寫代碼的時(shí)候,大家記得不記得線程池是如何構(gòu)造出來的呢?
是不是類似下面那樣的代碼,比如說我們構(gòu)造一個(gè)線程數(shù)量固定的一個(gè)線程池:
那么Executors.newFixedThreadPool(10)內(nèi)部到底又是如何構(gòu)造出來線程池的呢?
其實(shí)很簡(jiǎn)單,翻開JDK源碼就可以看到里面的代碼如下:
簡(jiǎn)單來說,就是構(gòu)造了一個(gè)ThreadPoolExecutor對(duì)象實(shí)例,你大致就認(rèn)為他是一個(gè)線程池吧,傳入了一些參數(shù),這些參數(shù)大致包含了:
1 · corePoolSize
2 · maximumPoolSize
3 · keepAliveTime
4 · workQueue
假如說我們構(gòu)造線程池傳入的線程數(shù)量是10,那么在這里,corePoolSize和maximumSize都是10,keepAliveTime默認(rèn)就是0,workQueue是一個(gè)無界的LinkedBlockingQueue。
接下來,我們具體來看看構(gòu)造一個(gè)線程池傳入一些參數(shù)之后,具體這個(gè)線程池的運(yùn)行原理是什么。
簡(jiǎn)單來說,剛開始的時(shí)候其實(shí)線程池里是空的,就是一個(gè)線程都沒有的,如下圖所示。
接著如果你使用線程池提交一個(gè)任務(wù)進(jìn)去,希望由線程池里的一個(gè)線程來執(zhí)行,如下代碼所示,就是提交一個(gè)任務(wù):
這個(gè)時(shí)候,線程池會(huì)先看一下,現(xiàn)在池子里的線程數(shù)量有沒有有達(dá)到corePoolSize指定的數(shù)量。
現(xiàn)在線程池里的線程數(shù)量是0,然后corePoolSize是10,那么肯定沒達(dá)到了,所以直接會(huì)在線程池里創(chuàng)建一個(gè)線程出來然后執(zhí)行這個(gè)任務(wù),如下圖。
接著假如說,這個(gè)線程處理完一個(gè)任務(wù)了,那么此時(shí)線程是不會(huì)被銷毀的,他會(huì)一直等待下一個(gè)提交過來的任務(wù)。
那么,到底是怎么等待的呢?
很簡(jiǎn)單,線程池會(huì)搭配一個(gè)workQueue,比如這里搭配的就是一個(gè)無界的LinkedBlockingQueue,幾乎可以無限量放入任務(wù)。
然后那個(gè)線程處理完一個(gè)任務(wù)之后,就會(huì)用阻塞的方式嘗試從任務(wù)隊(duì)列里獲取任務(wù),如果隊(duì)列是空的,他就會(huì)阻塞卡在那兒不動(dòng),直到有人放一個(gè)任務(wù)到隊(duì)列里,他才會(huì)獲取到一個(gè)任務(wù)然后繼續(xù)執(zhí)行,循環(huán)往復(fù),如下圖。
接著再次提交任務(wù),線程池一判斷發(fā)現(xiàn),誒?好像線程數(shù)量才只有1個(gè),完全比corePoolSize(10個(gè))要小,那么繼續(xù)直接在池子里創(chuàng)建一個(gè)線程,然后處理這個(gè)任務(wù),處理完了繼續(xù)嘗試從workQueue里阻塞式獲取任務(wù)。
一直重復(fù)上面的操作,直到線程池里有10個(gè)線程了,達(dá)到了corePoolSize指定的數(shù)量,如下圖。
這個(gè)時(shí)候你如果再提交任務(wù),他一下子發(fā)現(xiàn),誒?不對(duì)啊,線程池里已經(jīng)有10個(gè)線程了,跟corePoolSize指定的線程數(shù)量一樣了。
那么現(xiàn)在,我就不需要?jiǎng)?chuàng)建任何一個(gè)額外的線程了,現(xiàn)在你只要提交任務(wù),全部直接入隊(duì)到workQueue里就好。
此時(shí)線程池里的線程都阻塞式在workQueue上等待獲取任務(wù),有一個(gè)任務(wù)進(jìn)來就會(huì)喚醒一個(gè)線程來處理這個(gè)任務(wù),處理完了任務(wù)再次阻塞在workQueue上嘗試獲取下一個(gè)任務(wù),如下圖所示這個(gè)意思。
這里我們看到他用的是一個(gè)無界的LinkedBlockingQueue,但是假如說他用的是一個(gè)有界的隊(duì)列呢?
比如說限定好了隊(duì)列最多只能放10個(gè)任務(wù),那么假如說,線程池里的線程來不及處理任務(wù)了,然后隊(duì)列一下子放滿了10個(gè)任務(wù)。
此時(shí)就會(huì)出現(xiàn)任務(wù)入隊(duì)的失敗,因?yàn)殛?duì)列滿了,無法入隊(duì)。
然后就會(huì)嘗試再次在線程池里創(chuàng)建線程,這個(gè)時(shí)候就會(huì)一直創(chuàng)建線程直到線程池里的線程數(shù)量達(dá)到maximumPoolSize指定的數(shù)量為止。
雖然這里fixed線程池默認(rèn)corePoolSize和maximumPoolSize的數(shù)量都是一致的,但是可以假設(shè)此時(shí)maximumPoolSize的數(shù)量是20呢?
那么就會(huì)繼續(xù)創(chuàng)建線程,直到線程數(shù)量達(dá)到20個(gè),然后用額外創(chuàng)建的10個(gè)線程在隊(duì)列滿的情況下,繼續(xù)處理任務(wù)。
整個(gè)過程,如下圖所示:
接著萬一隊(duì)列滿了,然后線程池的線程數(shù)量達(dá)到了maximumPoolSize指定的數(shù)量了,你額外創(chuàng)建線程都無法創(chuàng)建了,此時(shí)會(huì)如何呢?
答案是:會(huì)reject掉,不讓你繼續(xù)提交任務(wù)了,此時(shí)默認(rèn)的就是拋出一個(gè)異常。
那么,在上圖中額外創(chuàng)建出來的,超出corePoolSize的那些線程呢?
他們一旦創(chuàng)建出來之后,會(huì)發(fā)現(xiàn)線程池?cái)?shù)量已經(jīng)超過corePoolSize了,此時(shí)他們會(huì)嘗試等待workQueue里的任務(wù)。
一旦超過keepAliveTime指定的時(shí)間,還獲取不到任務(wù),比如keepAliveTime是60秒,那么假如超過60秒獲取不到任務(wù),他就會(huì)自動(dòng)釋放掉了,這個(gè)線程就銷毀了。
整個(gè)過程,如下圖所示。
明白了線程池的運(yùn)行原理了,這個(gè)面試題就好解答了。
我們以最常用的fixed線程池舉例,他的線程池?cái)?shù)量是固定的,因?yàn)樗玫氖墙跤跓o界的LinkedBlockingQueue,幾乎可以無限制的放入任務(wù)到隊(duì)列里。
所以只要線程池里的線程數(shù)量達(dá)到了corePoolSize指定的數(shù)量之后,接下來就維持這個(gè)固定數(shù)量的線程了。
然后,所有任務(wù)都會(huì)入隊(duì)到workQueue里去,線程從workQueue獲取任務(wù)來處理。
這個(gè)隊(duì)列幾乎永遠(yuǎn)不會(huì)滿,當(dāng)然這是幾乎,因?yàn)長(zhǎng)inkedBlockingQueue默認(rèn)的最大任務(wù)數(shù)量是Integer.MAX_VALUE,非常大,近乎于可以理解為無限吧。
只要隊(duì)列不滿,就跟maximumPoolSize、keepAliveTime這些沒關(guān)系了,因?yàn)椴粫?huì)創(chuàng)建超過corePoolSize數(shù)量的線程的。
同樣,給大家來一張圖,我們來看看:
那么此時(shí)萬一每個(gè)線程獲取到一個(gè)任務(wù)之后,他處理的時(shí)間特別特別的長(zhǎng),長(zhǎng)到了令人發(fā)指的地步。比如處理一個(gè)任務(wù)要幾個(gè)小時(shí),此時(shí)會(huì)如何?
當(dāng)然會(huì)出現(xiàn)workQueue里不斷的積壓越來越多得任務(wù),不停的增加。
這個(gè)過程中會(huì)導(dǎo)致機(jī)器的內(nèi)存使用不停的飆升,最后也許極端情況下就導(dǎo)致JVM OOM了,系統(tǒng)就掛掉了。
所以這就是這個(gè)面試題背后你要知道的線程池的運(yùn)行原理,以及可能遇到的一些問題,大家要做到心里有數(shù)。
到此,相信大家對(duì)“使用Java無界隊(duì)列的線程池會(huì)怎么樣”有了更深的了解,不妨來實(shí)際操作一番吧!這里是億速云網(wǎng)站,更多相關(guān)內(nèi)容可以進(jìn)入相關(guān)頻道進(jìn)行查詢,關(guān)注我們,繼續(xù)學(xué)習(xí)!
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。