一篇文章搞清楚Java線程池泌神!知識(shí)點(diǎn)整理及經(jīng)典面試題剖析良漱,必看!;都省母市!

1、什么是線程池

線程池的基本思想是一種對(duì)象池损趋,在程序啟動(dòng)時(shí)就開(kāi)辟一塊內(nèi)存空間患久,里面存放了眾多(未死亡)的線程,池中線程執(zhí)行調(diào)度由池管理器來(lái)處理。當(dāng)有線程任務(wù)時(shí)蒋失,從池中取一個(gè)返帕,執(zhí)行完成后線程對(duì)象歸池,這樣可以避免反復(fù)創(chuàng)建線程對(duì)象所帶來(lái)的性能開(kāi)銷高镐,節(jié)省了系統(tǒng)的資源溉旋。

記得點(diǎn)贊收藏加關(guān)注哦 畸冲,我這里也準(zhǔn)備了很多面試熱門知識(shí)點(diǎn)和大廠面試題嫉髓,希望對(duì)大家有幫助!有需要的朋友

可以加q群:580763979? ? ??備注:簡(jiǎn)書? ?免費(fèi)領(lǐng)取~

2邑闲、使用線程池的好處

減少了創(chuàng)建和銷毀線程的次數(shù)算行,每個(gè)工作線程都可以被重復(fù)利用,可執(zhí)行多個(gè)任務(wù)苫耸。

運(yùn)用線程池能有效的控制線程最大并發(fā)數(shù)州邢,可以根據(jù)系統(tǒng)的承受能力,調(diào)整線程池中工作線線程的數(shù)目褪子,防止因?yàn)橄倪^(guò)多的內(nèi)存量淌,而把服務(wù)器累趴下(每個(gè)線程需要大約1MB內(nèi)存,線程開(kāi)的越多嫌褪,消耗的內(nèi)存也就越大呀枢,最后死機(jī))。

對(duì)線程進(jìn)行一些簡(jiǎn)單的管理笼痛,比如:延時(shí)執(zhí)行裙秋、定時(shí)循環(huán)執(zhí)行的策略等,運(yùn)用線程池都能進(jìn)行很好的實(shí)現(xiàn)

3缨伊、線程池的主要組件

一個(gè)線程池包括以下四個(gè)基本組成部分:

線程池管理器(ThreadPool):用于創(chuàng)建并管理線程池摘刑,包括 創(chuàng)建線程池,銷毀線程池刻坊,添加新任務(wù)枷恕;

工作線程(WorkThread):線程池中線程,在沒(méi)有任務(wù)時(shí)處于等待狀態(tài)谭胚,可以循環(huán)的執(zhí)行任務(wù)活尊;

任務(wù)接口(Task):每個(gè)任務(wù)必須實(shí)現(xiàn)的接口,以供工作線程調(diào)度任務(wù)的執(zhí)行漏益,它主要規(guī)定了任務(wù)的入口蛹锰,任務(wù)執(zhí)行完后的收尾工作,任務(wù)的執(zhí)行狀態(tài)等绰疤;

任務(wù)隊(duì)列(taskQueue):用于存放沒(méi)有處理的任務(wù)铜犬。提供一種緩沖機(jī)制。

4、線程池參數(shù)設(shè)置

參數(shù)的設(shè)置跟系統(tǒng)的負(fù)載有直接的關(guān)系癣猾,下面為系統(tǒng)負(fù)載的相關(guān)參數(shù):

tasks敛劝,每秒需要處理的的任務(wù)數(shù)(針對(duì)系統(tǒng)需求)

threadtasks,每個(gè)線程每鈔可處理任務(wù)數(shù)(針對(duì)線程本身)

responsetime纷宇,系統(tǒng)允許任務(wù)最大的響應(yīng)時(shí)間夸盟,比如每個(gè)任務(wù)的響應(yīng)時(shí)間不得超過(guò)2秒。

corePoolSize

系統(tǒng)每秒有tasks個(gè)任務(wù)需要處理理像捶,則每個(gè)線程每鈔可處理threadtasks個(gè)任務(wù)上陕。,則需要的線程數(shù)為:tasks/threadtasks拓春,即tasks/threadtasks個(gè)線程數(shù)释簿。

假設(shè)系統(tǒng)每秒任務(wù)數(shù)為100 ~ 1000,每個(gè)線程每鈔可處理10個(gè)任務(wù)硼莽,則需要100 / 10至1000 / 10庶溶,即10 ~ 100個(gè)線程。那么corePoolSize應(yīng)該設(shè)置為大于10懂鸵,具體數(shù)字最好根據(jù)8020原則偏螺,因?yàn)橄到y(tǒng)每秒任務(wù)數(shù)為100 ~ 1000,即80%情況下系統(tǒng)每秒任務(wù)數(shù)小于1000 * 20% = 200匆光,則corePoolSize可設(shè)置為200 / 10 = 20套像。

queueCapacity

任務(wù)隊(duì)列的長(zhǎng)度要根據(jù)核心線程數(shù),以及系統(tǒng)對(duì)任務(wù)響應(yīng)時(shí)間的要求有關(guān)殴穴。隊(duì)列長(zhǎng)度可以設(shè)置為 所有核心線程每秒處理任務(wù)數(shù) * 每個(gè)任務(wù)響應(yīng)時(shí)間 = 每秒任務(wù)總響應(yīng)時(shí)間 凉夯,即(corePoolSize*threadtasks)responsetime: (2010)*2=400,即隊(duì)列長(zhǎng)度可設(shè)置為400采幌。

maxPoolSize

當(dāng)系統(tǒng)負(fù)載達(dá)到最大值時(shí)劲够,核心線程數(shù)已無(wú)法按時(shí)處理完所有任務(wù),這時(shí)就需要增加線程休傍。每秒200個(gè)任務(wù)需要20個(gè)線程征绎,那么當(dāng)每秒達(dá)到1000個(gè)任務(wù)時(shí),則需要(tasks - queueCapacity)/ threadtasks 即(1000-400)/10磨取,即60個(gè)線程人柿,可將maxPoolSize設(shè)置為60。

隊(duì)列長(zhǎng)度設(shè)置過(guò)大忙厌,會(huì)導(dǎo)致任務(wù)響應(yīng)時(shí)間過(guò)長(zhǎng)凫岖,切忌以下寫法:

LinkedBlockingQueue queue = new LinkedBlockingQueue();


這實(shí)際上是將隊(duì)列長(zhǎng)度設(shè)置為Integer.MAX_VALUE,將會(huì)導(dǎo)致線程數(shù)量永遠(yuǎn)為corePoolSize逢净,再也不會(huì)增加哥放,當(dāng)任務(wù)數(shù)量陡增時(shí)歼指,任務(wù)響應(yīng)時(shí)間也將隨之陡增。

keepAliveTime

當(dāng)負(fù)載降低時(shí)甥雕,可減少線程數(shù)量踩身,當(dāng)線程的空閑時(shí)間超過(guò)keepAliveTime,會(huì)自動(dòng)釋放線程資源社露。默認(rèn)情況下線程池停止多余的線程并最少會(huì)保持corePoolSize個(gè)線程挟阻。

allowCoreThreadTimeout

默認(rèn)情況下核心線程不會(huì)退出,可通過(guò)將該參數(shù)設(shè)置為true峭弟,讓核心線程也退出附鸽。

一般說(shuō)來(lái),大家認(rèn)為線程池的大小經(jīng)驗(yàn)值應(yīng)該這樣設(shè)置:(其中N為CPU的個(gè)數(shù))

如果是CPU密集型應(yīng)用孟害,則線程池大小設(shè)置為N+1

如果是IO密集型應(yīng)用拒炎,則線程池大小設(shè)置為2N+1

5挪拟、線程池的五種狀態(tài)

線程池的初始化狀態(tài)是RUNNING挨务,能夠接收新任務(wù),以及對(duì)已添加的任務(wù)進(jìn)行處理玉组。

線程池處在SHUTDOWN狀態(tài)時(shí)谎柄,不接收新任務(wù),但能處理已添加的任務(wù)惯雳。 調(diào)用線程池的shutdown()接口時(shí)朝巫,線程池由RUNNING -> SHUTDOWN。

線程池處在STOP狀態(tài)時(shí)石景,不接收新任務(wù)劈猿,不處理已添加的任務(wù),并且會(huì)中斷正在處理的任務(wù)潮孽。 調(diào)用線程池的shutdownNow()接口時(shí)揪荣,線程池由(RUNNING or SHUTDOWN ) -> STOP。

當(dāng)所有的任務(wù)已終止往史,ctl記錄的”任務(wù)數(shù)量”為0仗颈,線程池會(huì)變?yōu)門IDYING狀態(tài)。當(dāng)線程池變?yōu)門IDYING狀態(tài)時(shí)椎例,會(huì)執(zhí)行鉤子函數(shù)terminated()挨决。terminated()在ThreadPoolExecutor類中是空的,若用戶想在線程池變?yōu)門IDYING時(shí)订歪,進(jìn)行相應(yīng)的處理脖祈;可以通過(guò)重載terminated()函數(shù)來(lái)實(shí)現(xiàn)。

當(dāng)線程池在SHUTDOWN狀態(tài)下刷晋,阻塞隊(duì)列為空并且線程池中執(zhí)行的任務(wù)也為空時(shí)盖高,就會(huì)由 SHUTDOWN -> TIDYING福压。

當(dāng)線程池在STOP狀態(tài)下,線程池中執(zhí)行的任務(wù)為空時(shí)或舞,就會(huì)由STOP -> TIDYING荆姆。 線程池徹底終止,就變成TERMINATED狀態(tài)映凳。線程池處在TIDYING狀態(tài)時(shí)胆筒,執(zhí)行完terminated()之后,就會(huì)由 TIDYING -> TERMINATED诈豌。

6仆救、關(guān)閉線程池

線程池提供兩種關(guān)閉線程池方法:shutDown()和shutdownNow()

shutDown()

當(dāng)線程池調(diào)用該方法時(shí),線程池的狀態(tài)則立刻變成SHUTDOWN狀態(tài)。此時(shí)矫渔,則不能再往線程池中添加任何任務(wù)彤蔽,否則將會(huì)拋出RejectedExecutionException異常。但是庙洼,此時(shí)線程池不會(huì)立刻退出顿痪,直到添加到線程池中的任務(wù)都已經(jīng)處理完成,才會(huì)退出油够。

shutdownNow()

根據(jù)JDK文檔描述蚁袭,大致意思是:執(zhí)行該方法,線程池的狀態(tài)立刻變成STOP狀態(tài)石咬,并試圖停止所有正在執(zhí)行的線程揩悄,不再處理還在池隊(duì)列中等待的任務(wù),當(dāng)然鬼悠,它會(huì)返回那些未執(zhí)行的任務(wù)删性。

它試圖終止線程的方法是通過(guò)調(diào)用Thread.interrupt()方法來(lái)實(shí)現(xiàn)的,但是大家知道焕窝,這種方法的作用有限蹬挺,如果線程中沒(méi)有sleep、wait袜啃、Condition汗侵、定時(shí)鎖等應(yīng)用, interrupt()方法是無(wú)法中斷當(dāng)前的線程的。所以群发,ShutdownNow()并不代表線程池就一定立即就能退出晰韵,它可能必須要等待所有正在執(zhí)行的任務(wù)都執(zhí)行完成了才能退出。

7熟妓、各種場(chǎng)景下怎么設(shè)置線程數(shù)

高并發(fā)雪猪、任務(wù)執(zhí)行時(shí)間短的業(yè)務(wù)怎樣使用線程池?

線程池線程數(shù)可以設(shè)置為CPU核數(shù)+1起愈,減少線程上下文的切換

并發(fā)不高只恨、任務(wù)執(zhí)行時(shí)間長(zhǎng)的業(yè)務(wù)怎樣使用線程池译仗?

這個(gè)需要判斷執(zhí)行時(shí)間是耗在哪個(gè)地方

假如是業(yè)務(wù)時(shí)間長(zhǎng)集中在IO操作上,也就是IO密集型的任務(wù)官觅,因?yàn)镮O操作并不占用CPU纵菌,所以不要讓所有的CPU閑下來(lái),可以適當(dāng)加大線程池中的線程數(shù)目(2 * CPU核數(shù))休涤,讓CPU處理更多的業(yè)務(wù)咱圆。

假如是業(yè)務(wù)時(shí)間長(zhǎng)集中在計(jì)算操作上,也就是CPU密集型任務(wù)功氨,和(1)CPU核數(shù)+1 一樣吧序苏,線程池中的線程數(shù)設(shè)置得少一些,減少線程上下文的切換

并發(fā)高捷凄、業(yè)務(wù)執(zhí)行時(shí)間長(zhǎng)的業(yè)務(wù)怎樣使用線程池忱详?

解決這種類型任務(wù)的關(guān)鍵不在于線程池而在于整體架構(gòu)的設(shè)計(jì)

8、為什么不推薦使用JUC的線程池跺涤?

這樣的處理方式更加明確線程池的運(yùn)行規(guī)則匈睁,規(guī)避資源耗盡的風(fēng)險(xiǎn)

newFixedThreadPool和newSingleThreadExecutor

上面兩個(gè)主要問(wèn)題是堆積的請(qǐng)求處理隊(duì)列可能會(huì)耗費(fèi)非常大的內(nèi)存,甚至OOM


newCachedThreadPool和newScheduledThreadPool

上面兩個(gè)主要問(wèn)題是最大線程數(shù)是Integer.MAX_VALUE钦铁,可能會(huì)創(chuàng)建數(shù)量非常多的線程软舌,甚至OOM才漆。

9牛曹、問(wèn)題

非核心線程延遲死亡,如何實(shí)現(xiàn)醇滥?

通過(guò)阻塞隊(duì)列poll()黎比,讓線程阻塞等待一段時(shí)間,如果沒(méi)有取到任務(wù)鸳玩,則線程死亡

線程池為什么能維持線程不釋放阅虫,隨時(shí)運(yùn)行各種任務(wù)?

for (;;) {


? ? ? ? ? ? try {

? ? ? ? ? ? ? ? Runnable r = timed ?

? ? ? ? ? ? ? ? ? ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :

? ? ? ? ? ? ? ? ? ? workQueue.take();

? ? ? ? ? ? ? ? if (r != null)

? ? ? ? ? ? ? ? ? ? return r;

? ? ? ? ? ? ? ? timedOut = true;

? ? ? ? ? ? } catch (InterruptedException retry) {

? ? ? ? ? ? ? ? timedOut = false;

? ? ? ? ? ? }

? ? ? ? }

? ? }

在死循環(huán)中工作隊(duì)列workQueue會(huì)一直去拿任務(wù):

核心線程的會(huì)一直卡在 workQueue.take()方法不跟,讓線程一直等待颓帝,直到獲取到任務(wù),然后返回窝革。

非核心線程會(huì) workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) 购城,如果超時(shí)還沒(méi)有拿到,下一次循環(huán)判斷compareAndDecrementWorkerCount就會(huì)返回null,Worker對(duì)象的run()方法循環(huán)體的判斷為null,任務(wù)結(jié)束虐译,然后線程被系統(tǒng)回收瘪板。

通過(guò)阻塞隊(duì)列take(),讓線程一直等待漆诽,直到獲取到任務(wù)

如何釋放核心線程侮攀?

將allowCoreThreadTimeOut設(shè)置為true锣枝。可用下面代碼實(shí)驗(yàn)

{

? ? // 允許釋放核心線程兰英,等待時(shí)間為100毫秒

? ? es.allowCoreThreadTimeOut(true);

? ? for(......){

? ? ? ? // 向線程池里添加任務(wù)撇叁,任務(wù)內(nèi)容為打印當(dāng)前線程池線程數(shù)

? ? ? ? Thread.currentThread().sleep(200);

? ? }

}

線程數(shù)會(huì)一直為1。 如果allowCoreThreadTimeOut為false畦贸,線程數(shù)會(huì)逐漸達(dá)到飽和税朴,然后大家一起阻塞等待。

非核心線程能成為核心線程嗎家制?

線程池不區(qū)分核心線程于非核心線程正林,只是根據(jù)當(dāng)前線程池容量狀態(tài)做不同的處理來(lái)進(jìn)行調(diào)整,因此看起來(lái)像是有核心線程于非核心線程颤殴,實(shí)際上是滿足線程池期望達(dá)到的并發(fā)狀態(tài)觅廓。

Runnable在線程池里如何執(zhí)行?

線程執(zhí)行Worker涵但,Worker不斷從阻塞隊(duì)列里獲取任務(wù)來(lái)執(zhí)行杈绸。。

總結(jié)

我這里準(zhǔn)備了一線大廠面試資料和超多超硬核的PDF技術(shù)文檔矮瘟,以及我為大家精心準(zhǔn)備的多套簡(jiǎn)歷模板(不斷更新中)瞳脓,希望大家都能找到心儀的工作!

有需要的朋友可以加q群:580763979? ? ??備注:簡(jiǎn)書? ?免費(fèi)領(lǐng)取~

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末澈侠,一起剝皮案震驚了整個(gè)濱河市劫侧,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌哨啃,老刑警劉巖烧栋,帶你破解...
    沈念sama閱讀 212,718評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異拳球,居然都是意外死亡审姓,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門祝峻,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)魔吐,“玉大人,你說(shuō)我怎么就攤上這事莱找〕昴罚” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 158,207評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵宋距,是天一觀的道長(zhǎng)轴踱。 經(jīng)常有香客問(wèn)我,道長(zhǎng)谚赎,這世上最難降的妖魔是什么淫僻? 我笑而不...
    開(kāi)封第一講書人閱讀 56,755評(píng)論 1 284
  • 正文 為了忘掉前任诱篷,我火速辦了婚禮,結(jié)果婚禮上雳灵,老公的妹妹穿的比我還像新娘棕所。我一直安慰自己,他們只是感情好悯辙,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布琳省。 她就那樣靜靜地躺著,像睡著了一般躲撰。 火紅的嫁衣襯著肌膚如雪针贬。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書人閱讀 50,050評(píng)論 1 291
  • 那天拢蛋,我揣著相機(jī)與錄音桦他,去河邊找鬼。 笑死谆棱,一個(gè)胖子當(dāng)著我的面吹牛快压,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播垃瞧,決...
    沈念sama閱讀 39,136評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼蔫劣,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了个从?” 一聲冷哼從身側(cè)響起脉幢,我...
    開(kāi)封第一講書人閱讀 37,882評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎信姓,沒(méi)想到半個(gè)月后鸵隧,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,330評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡意推,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了珊蟀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片菊值。...
    茶點(diǎn)故事閱讀 38,789評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖育灸,靈堂內(nèi)的尸體忽然破棺而出腻窒,到底是詐尸還是另有隱情,我是刑警寧澤磅崭,帶...
    沈念sama閱讀 34,477評(píng)論 4 333
  • 正文 年R本政府宣布儿子,位于F島的核電站,受9級(jí)特大地震影響砸喻,放射性物質(zhì)發(fā)生泄漏柔逼。R本人自食惡果不足惜蒋譬,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評(píng)論 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望愉适。 院中可真熱鬧犯助,春花似錦、人聲如沸维咸。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,864評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)癌蓖。三九已至瞬哼,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間租副,已是汗流浹背倒槐。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,099評(píng)論 1 267
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留附井,地道東北人讨越。 一個(gè)月前我還...
    沈念sama閱讀 46,598評(píng)論 2 362
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像永毅,于是被迫代替她去往敵國(guó)和親把跨。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容