談?wù)劸€程池吧

????大家在面試時(shí)候都會(huì)經(jīng)常被問到惰说,簡(jiǎn)單介紹一下線程池吧磨德;線程池參數(shù)有哪些缘回;你們公司線程池怎么配置的啊吆视。諸如此類的問題,線程池又在我們多線程開發(fā)中多次用到酥宴,可謂不是問jvm那種紙上談兵的技術(shù)啦吧,今天就帶大家簡(jiǎn)單回顧一下線程的知識(shí),教你如何面對(duì)面試官的連環(huán)炮拙寡。

? ? 首先授滓,阿里巴巴規(guī)范上明確指出:

【強(qiáng)制】線程池不允許使用Executors去創(chuàng)建,而是通過ThreadPoolExecutor的方式肆糕,這樣的處理方式讓寫的同學(xué)更加明確線程池的運(yùn)行規(guī)則般堆,規(guī)避資源耗盡的風(fēng)險(xiǎn)。

說明:Executors各個(gè)方法的弊端:

1)newFixedThreadPool和newSingleThreadExecutor:主要問題是堆積的請(qǐng)求處理隊(duì)列可能會(huì)耗費(fèi)非常大的內(nèi)存诚啃,甚至OOM淮摔。

2)newCachedThreadPool和newScheduledThreadPool:主要問題是線程數(shù)最大數(shù)是Integer.MAX_VALUE,可能會(huì)創(chuàng)建數(shù)量非常多的線程始赎,甚至OOM和橙。

那我們來看看ThreadPoolExecutor類

java.uitl.concurrent.ThreadPoolExecutor類是線程池中最核心的一個(gè)類,因此如果要透徹地了解Java中的線程池造垛,必須先了解這個(gè)類魔招。下面我們來看一下ThreadPoolExecutor類的具體實(shí)現(xiàn)源碼。

public class ThreadPoolExecutor extends AbstractExecutorService {

? ? .....

? ? public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,

? ? ? ? ? ? BlockingQueue<Runnable> workQueue);

? ? public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,

? ? ? ? ? ? BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory);

? ? public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,

? ? ? ? ? ? BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler);

? ? public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,

? ? ? ? BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler);

? ? ...

}

事實(shí)上五辽,通過觀察每個(gè)構(gòu)造器的源碼具體實(shí)現(xiàn)办斑,發(fā)現(xiàn)前面三個(gè)構(gòu)造器都是調(diào)用的第四個(gè)構(gòu)造器進(jìn)行的初始化工作。

下面我們看看參數(shù)含義杆逗,先上Doug Lea老人家自己定義的含義標(biāo)準(zhǔn)乡翅,


1.corePoolSize:核心池的大小,這個(gè)參數(shù)跟后面講述的線程池的實(shí)現(xiàn)原理有非常大的關(guān)系髓迎。在創(chuàng)建了線程池后峦朗,默認(rèn)情況下,線程池中并沒有任何線程排龄,而是等待有任務(wù)到來才創(chuàng)建線程去執(zhí)行任務(wù)波势,除非調(diào)用了prestartAllCoreThreads()或者prestartCoreThread()方法(線程池預(yù)熱翎朱,面對(duì)大流量的系統(tǒng)),從這2個(gè)方法的名字就可以看出尺铣,是預(yù)創(chuàng)建線程的意思拴曲,即在沒有任務(wù)到來之前就創(chuàng)建corePoolSize個(gè)線程或者一個(gè)線程。默認(rèn)情況下凛忿,在創(chuàng)建了線程池后澈灼,線程池中的線程數(shù)為0,當(dāng)有任務(wù)來之后店溢,就會(huì)創(chuàng)建一個(gè)線程去執(zhí)行任務(wù)叁熔,當(dāng)線程池中的線程數(shù)目達(dá)到corePoolSize后,就會(huì)把到達(dá)的任務(wù)放到緩存隊(duì)列當(dāng)中床牧;

2.maximumPoolSize:線程池最大線程數(shù)荣回,這個(gè)參數(shù)也是一個(gè)非常重要的參數(shù),它表示在線程池中最多能創(chuàng)建多少個(gè)線程戈咳;

3.keepAliveTime:如果經(jīng)過 keepAliveTime 時(shí)間后心软,超過核心線程數(shù)的線程還沒有接受到新的任務(wù),那就回收著蛙。默認(rèn)情況下删铃,只有當(dāng)線程池中的線程數(shù)大于corePoolSize時(shí),keepAliveTime才會(huì)起作用踏堡,直到線程池中的線程數(shù)不大于corePoolSize猎唁,即當(dāng)線程池中的線程數(shù)大于corePoolSize時(shí),如果一個(gè)線程空閑的時(shí)間達(dá)到keepAliveTime暂吉,則會(huì)終止胖秒,直到線程池中的線程數(shù)不超過corePoolSize。注意:但是如果調(diào)用了allowCoreThreadTimeOut(boolean)方法慕的,在線程池中的線程數(shù)不大于corePoolSize時(shí)阎肝,keepAliveTime參數(shù)也會(huì)起作用,直到線程池中的線程數(shù)為0肮街;

4.unit:參數(shù)keepAliveTime的時(shí)間單位风题,有7種取值。TimeUnit.DAYS嫉父、TimeUnit.HOURS沛硅、TimeUnit.MINUTES、TimeUnit.SECONDS绕辖、TimeUnit.MILLISECONDS摇肌、TimeUnit.MICROSECONDS、TimeUnit.NANOSECONDS

5.workQueue:一個(gè)阻塞隊(duì)列仪际,用來存儲(chǔ)等待執(zhí)行的任務(wù)围小,這個(gè)參數(shù)的選擇也很重要昵骤,會(huì)對(duì)線程池的運(yùn)行過程產(chǎn)生重大影響,一般來說肯适,這里的阻塞隊(duì)列有以下幾種選擇:ArrayBlockingQueue变秦、LinkedBlockingQueue、SynchronousQueue框舔。?

ArrayBlockingQueue和PriorityBlockingQueue使用較少蹦玫,一般使用LinkedBlockingQueue和Synchronous。線程池的排隊(duì)策略與BlockingQueue有關(guān)刘绣。

6.threadFactory:線程工廠樱溉,主要用來創(chuàng)建線程,比如這里面可以自定義線程名稱额港,當(dāng)進(jìn)行虛擬機(jī)棧分析時(shí)饺窿,看著名字就知道這個(gè)線程是哪里來的歧焦;

7.handler:當(dāng)隊(duì)列里面放滿了任務(wù)移斩、最大線程數(shù)的線程都在工作時(shí),這時(shí)繼續(xù)提交的任務(wù)線程池就處理不了绢馍,應(yīng)該執(zhí)行怎么樣的拒絕策略向瓷,有以下四種取值:?

ThreadPoolExecutor.AbortPolicy:丟棄任務(wù)并拋出RejectedExecutionException異常。?

ThreadPoolExecutor.DiscardPolicy:也是丟棄任務(wù)舰涌,但是不拋出異常猖任。?

ThreadPoolExecutor.DiscardOldestPolicy:丟棄隊(duì)列最前面的任務(wù),然后重新嘗試執(zhí)行任務(wù)(重復(fù)此過程)?

ThreadPoolExecutor.CallerRunsPolicy:由調(diào)用線程處理該任務(wù)


????講完參數(shù)的含義了瓷耙,下面開始介紹公司怎么配置線程池參數(shù)了:

主要需要關(guān)心的參數(shù)是:

corePoolSize朱躺、maximumPoolSize、workQueue(隊(duì)列長度)

你以為我要給你講分 IO 密集型任務(wù)或者分 CPU 密集型任務(wù)搁痛?不长搀,不是的我們要給面試官一個(gè)眼前一亮,與眾不同的答案鸡典,就如同漆黑中的螢火蟲一樣鮮明,一樣出眾源请。 - -?線程池參數(shù)動(dòng)態(tài)化。

先說現(xiàn)在線程池參數(shù)設(shè)定網(wǎng)上大多數(shù)的答案都是先區(qū)分線程池中的任務(wù)是 IO 密集型還是 CPU 密集型彻况。

如果是 CPU 密集型的谁尸,可以把核心線程數(shù)設(shè)置為核心數(shù)+1。

如果是?IO 密集型纽甘,可以把核心線程數(shù)設(shè)置為核心數(shù)*2良蛮。多么簡(jiǎn)潔,多么有力悍赢,但是一丟生產(chǎn)環(huán)境上决瞳,啊咬展,傻眼了。

本文給出的動(dòng)態(tài)化配置又是怎么配置的呢:

先上使用動(dòng)態(tài)更新的代碼示例:

看到43瞒斩,44行的代碼了么破婆,

excutor.?setCorePoolSize(10);? ?excutor.?setMaximumPoolSize(10);?

這就是精髓所在。

上一個(gè)setCorePoolSize流程圖:


再來setCorePoolSize源碼:


在運(yùn)行期線程池使用方調(diào)用此方法設(shè)置corePoolSize之后胸囱,線程池會(huì)直接覆蓋原來的corePoolSize值祷舀,并且基于當(dāng)前值和原始值的比較結(jié)果采取不同的處理策略。

對(duì)于當(dāng)前值小于當(dāng)前工作線程數(shù)的情況烹笔,說明有多余的worker線程裳扯,此時(shí)會(huì)向當(dāng)前idle的worker線程發(fā)起中斷請(qǐng)求以實(shí)現(xiàn)回收,多余的worker在下次idel的時(shí)候也會(huì)被回收谤职;

對(duì)于當(dāng)前值大于原始值且當(dāng)前隊(duì)列中有待執(zhí)行任務(wù)饰豺,則線程池會(huì)創(chuàng)建新的worker線程來執(zhí)行隊(duì)列任務(wù)≡黍冢恍然大悟冤吨,原來我們一直用的核心線程數(shù)是可以配置的!的確饶套,翻開源碼漩蟆,的確是可變的。

接著看 setMaximumPoolSize 源碼


1.首先是參數(shù)合法性校驗(yàn)妓蛮。

2.然后用傳遞進(jìn)來的值怠李,覆蓋原來的值。

3.判斷工作線程是否是大于最大線程數(shù)蛤克,如果大于捺癞,則對(duì)空閑線程發(fā)起中斷請(qǐng)求。


設(shè)置核心線程數(shù)的時(shí)候构挤,同時(shí)設(shè)置最大線程數(shù)即可髓介。其實(shí)可以把二者設(shè)置為相同的值。

當(dāng)時(shí)有個(gè)疑惑:如果調(diào)整之后把活動(dòng)線程數(shù)設(shè)置的值太大了儿倒,豈不是業(yè)務(wù)低峰期我們還需要人工把值調(diào)的小一點(diǎn)版保?

不存在的,還記得前面介紹 corePoolSize 參數(shù)的含義時(shí)的注解嗎:


當(dāng) allowCoreThreadTimeOut 參數(shù)設(shè)置為 true 的時(shí)候夫否,核心線程在空閑了 keepAliveTime 的時(shí)間后也會(huì)被回收的彻犁,相當(dāng)于線程池自動(dòng)給你動(dòng)態(tài)修改了。

好了凰慈,希望大家能給面試官一個(gè)痛擊汞幢,讓他有被shock到。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末微谓,一起剝皮案震驚了整個(gè)濱河市森篷,隨后出現(xiàn)的幾起案子输钩,更是在濱河造成了極大的恐慌,老刑警劉巖仲智,帶你破解...
    沈念sama閱讀 221,820評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件买乃,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡钓辆,警方通過查閱死者的電腦和手機(jī)剪验,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來前联,“玉大人功戚,你說我怎么就攤上這事∷凄停” “怎么了啸臀?”我有些...
    開封第一講書人閱讀 168,324評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長烁落。 經(jīng)常有香客問我乘粒,道長,這世上最難降的妖魔是什么顽馋? 我笑而不...
    開封第一講書人閱讀 59,714評(píng)論 1 297
  • 正文 為了忘掉前任谓厘,我火速辦了婚禮,結(jié)果婚禮上寸谜,老公的妹妹穿的比我還像新娘。我一直安慰自己属桦,他們只是感情好熊痴,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著聂宾,像睡著了一般果善。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上系谐,一...
    開封第一講書人閱讀 52,328評(píng)論 1 310
  • 那天巾陕,我揣著相機(jī)與錄音,去河邊找鬼纪他。 笑死鄙煤,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的茶袒。 我是一名探鬼主播梯刚,決...
    沈念sama閱讀 40,897評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼薪寓!你這毒婦竟也來了亡资?” 一聲冷哼從身側(cè)響起澜共,我...
    開封第一講書人閱讀 39,804評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎锥腻,沒想到半個(gè)月后嗦董,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,345評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瘦黑,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評(píng)論 3 340
  • 正文 我和宋清朗相戀三年展懈,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片供璧。...
    茶點(diǎn)故事閱讀 40,561評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡存崖,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出睡毒,到底是詐尸還是另有隱情来惧,我是刑警寧澤,帶...
    沈念sama閱讀 36,238評(píng)論 5 350
  • 正文 年R本政府宣布演顾,位于F島的核電站供搀,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏钠至。R本人自食惡果不足惜葛虐,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評(píng)論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望棉钧。 院中可真熱鬧屿脐,春花似錦、人聲如沸宪卿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽佑钾。三九已至西疤,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間休溶,已是汗流浹背代赁。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留兽掰,地道東北人芭碍。 一個(gè)月前我還...
    沈念sama閱讀 48,983評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像禾进,于是被迫代替她去往敵國和親豁跑。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評(píng)論 2 359