ThreadPoolExecutor解析

為什么要使用線程池赖草?

引用自?http://ifeve.com/java-threadpool/?的說(shuō)明:

1.頻繁的創(chuàng)建和銷毀線程會(huì)影響系統(tǒng)的性能可柿,線程池可以通過(guò)重復(fù)利用已創(chuàng)建的線程來(lái)降低線程創(chuàng)建和銷毀造成的資源消耗;

2.提高響應(yīng)的速度棘伴。當(dāng)任務(wù)到達(dá)時(shí)蜗巧,任務(wù)可以不需要等到線程的創(chuàng)建就能立刻執(zhí)行任務(wù)芯咧。

3.提高線程的可管理性。線程是稀缺資源胡野,如果無(wú)限制的創(chuàng)建材失,不僅會(huì)消耗系統(tǒng)資源,還會(huì)降低系統(tǒng)的穩(wěn)定性硫豆,使用線程池可以進(jìn)行統(tǒng)一的分配龙巨,管理與監(jiān)控。

線程池的類圖:

線程池的類圖

ThreadPoolExecutor的構(gòu)造方法:

1.

構(gòu)造方法

corePoolSize:核心線程數(shù)量熊响,當(dāng)有新的任務(wù)提交時(shí)旨别,會(huì)在線程池創(chuàng)建一個(gè)新的線程,直到線程數(shù)量達(dá)到核心線程數(shù)量為止汗茄。

maximumPoolSize:線程池允許的最大線程數(shù)量秸弛。

keepAliveTime:當(dāng)線程池中線程的數(shù)量大于corePoolSize的時(shí)候,沒(méi)有新的任務(wù)提交的話剔难,超過(guò)corePoolSize所允許的等待時(shí)間胆屿。

unit:時(shí)間的單位∨脊可選的單位有天(DAYS),小時(shí)(HOURS)环鲤,分鐘(MINUTES)纯趋,毫秒(MILLISECONDS),微秒(MICROSECONDS)和毫微秒(NANOSECONDS)冷离。

workQueue:阻塞隊(duì)列吵冒。當(dāng)線程池中的線程數(shù)量已經(jīng)達(dá)到corePoolSize之后,再通過(guò)execute添加的新的任務(wù)會(huì)被加到workQueue隊(duì)列中西剥。

threadFactory:創(chuàng)建新線程的工廠痹栖。

handler:當(dāng)線程池和隊(duì)列都滿了的時(shí)候,執(zhí)行的一種策略瞭空,用來(lái)處理新提交的任務(wù)揪阿。有以下4種策略:

1.AbortPolicy:直接拋出異常(默認(rèn));

2.CallerRunsPolicy:使用調(diào)用者所在的線程來(lái)執(zhí)行任務(wù)咆畏;

3.DiscardOldestPolicy:丟棄阻塞隊(duì)列中考前的任務(wù)南捂,并執(zhí)行當(dāng)前的任務(wù);

4.DiscardPolicy:直接丟棄任務(wù)旧找;

SynchronousQueue:直接提交隊(duì)列溺健,該隊(duì)列沒(méi)有容量,每一個(gè)插入操作都要等待一個(gè)相應(yīng)的刪除操作钮蛛,反之鞭缭,每一個(gè)刪除操作都要等待對(duì)應(yīng)的插入操作剖膳。所以他不保存任務(wù),總是將任務(wù)提交給線程執(zhí)行岭辣,如果沒(méi)有空閑的線程吱晒,則創(chuàng)建新的線程,當(dāng)線程數(shù)量達(dá)到最大易结,則執(zhí)行拒絕策略枕荞。

ArrayBlockingQueue:有界任務(wù)隊(duì)列,線程池的線程數(shù)小于corePoolSize搞动,則創(chuàng)建新的線程躏精,大于corePoolSize,則將新的任務(wù)加入等待隊(duì)列鹦肿。若等待隊(duì)列已滿矗烛,則在總線程不大于maximumPoolSize下,創(chuàng)建新的線程執(zhí)行任務(wù)箩溃,大于maximumPoolSize則執(zhí)行拒絕策略瞭吃。

LinkedBlockingQueue:無(wú)界隊(duì)列,除非系統(tǒng)資源耗盡涣旨,否則不存在任務(wù)入隊(duì)失敗的情況歪架。線程池的線程數(shù)小于corePoolSize,則創(chuàng)建新的線程霹陡,大于corePoolSize和蚪,則將新的任務(wù)加入等待隊(duì)列。

PriorityBlockingQueue:優(yōu)先任務(wù)隊(duì)列,可以控制任務(wù)的執(zhí)行先后順序烹棉,是無(wú)界隊(duì)列攒霹。ArrayBlockingQueue,LinkedBlockingQueue都是按照先進(jìn)先出算法處理任務(wù)的浆洗,PriorityBlockingQueue可以根據(jù)任務(wù)自身的優(yōu)先順序先后執(zhí)行催束。

線程池的工作流程

線程池的工作流程

從上圖可以看出,當(dāng)一個(gè)新的任務(wù)提交到線程池的時(shí)候伏社,線程池的處理流程如下:

1.首先判斷核心線程池是否已滿抠刺?如果沒(méi)滿,則創(chuàng)建一個(gè)新的線程來(lái)執(zhí)行任務(wù)洛口,滿了進(jìn)入下一個(gè)流程矫付;

2.判斷隊(duì)列是否已滿?如果沒(méi)滿第焰,則將新提交的任務(wù)存儲(chǔ)在工作隊(duì)列里买优,滿了進(jìn)入下一個(gè)流程;

3.最后判斷整個(gè)線程池是否已滿?如果沒(méi)滿杀赢,則創(chuàng)建一個(gè)新的工作線程來(lái)執(zhí)行任務(wù)烘跺,滿了,則交給飽和策略處理此任務(wù)脂崔。

源碼分析:

上邊的過(guò)程作了一個(gè)大概的了解滤淳,下邊看一下源代碼的實(shí)現(xiàn):

execute()

1.workerCountOf(c) < corePoolSize

通過(guò)workerCountOf()方法拿到當(dāng)前活動(dòng)的線程數(shù),如果當(dāng)前活動(dòng)的線程數(shù)小于corePoolSize,則新建一個(gè)線程放入到線程池中砌左,并且把任務(wù)添加到該線程脖咐。

2. if(addWorker(command, true))

PS:addWorker中的第二個(gè)參數(shù)表示添加線程的數(shù)量是根據(jù)corePoolSize來(lái)判斷還是maximumPoolSize來(lái)判斷;如果為true,根據(jù)corePoolSize來(lái)判斷汇歹;如果為false屁擅,則根據(jù)maximumuPoolSize來(lái)判斷。

3.if(isRunning(c) && workQueue.offer(command))

如果當(dāng)前線程是運(yùn)行狀態(tài)并且任務(wù)添加到隊(duì)列成功

4.if (! isRunning(recheck) && remove(command))

這里是一個(gè)二次檢查产弹,因?yàn)槿腙?duì)之后狀態(tài)還是可能發(fā)生變化的派歌。

5.else if (workerCountOf(recheck) ==0)? ? ? ? ? addWorker(null, false);

獲取線程池中的有效線程數(shù),如果等于0痰哨,則執(zhí)行addWorker()方法胶果。

傳入的參數(shù)表示:

第一個(gè)參數(shù)null, 表示在線程池中創(chuàng)建一個(gè)線程,但是不啟動(dòng)斤斧;

第二個(gè)參數(shù)為false,將線程池的上限設(shè)置為maximumPoolSize早抠,添加線程時(shí)根據(jù)maximumPoolSize來(lái)判斷;

6.else if (!addWorker(command, false))? ? ? ?reject(command);

如果隊(duì)列失敗了撬讽,嘗試添加新的任務(wù)贝或,如果失敗則拒絕該任務(wù)。

總結(jié):簡(jiǎn)單來(lái)說(shuō)在執(zhí)行execute()方法時(shí)如果狀態(tài)一直是RUNNING時(shí)的執(zhí)行過(guò)程如下:

1.如果workerCount < corePoolSize锐秦,則創(chuàng)建并啟動(dòng)一個(gè)線程來(lái)執(zhí)行新提交的任務(wù);

2.如果workerCount >= corePoolSize,且線程池內(nèi)的阻塞隊(duì)列未滿盗忱,則將任務(wù)添加到該阻塞隊(duì)列中酱床;

3.如果workerCount >= corePoolSize && workerCount < maximumPoolSize, 且線程池內(nèi)的阻塞隊(duì)列已滿,則創(chuàng)建并啟動(dòng)一個(gè)線程來(lái)執(zhí)行新提交的任務(wù)趟佃;

4.如果workerCount >= maximumPoolSize,并且線程池內(nèi)的阻塞隊(duì)列已滿扇谣,則根據(jù)拒絕策略來(lái)處理該任務(wù)(默認(rèn)處理方式直接拋出異常);

PS:這里addWorker(null, false); 也就是創(chuàng)建一個(gè)線程闲昭,但是并沒(méi)有傳入任務(wù)罐寨,因?yàn)槿蝿?wù)已經(jīng)被添加到workQueue中了,所以worker在執(zhí)行的時(shí)候序矩,會(huì)直接從workQueue中獲取任務(wù)鸯绿。因此當(dāng)workerCountOf(recheck) == 0的時(shí)候執(zhí)行addWorker(null,false)也是為了保證線程池在RUNNING狀態(tài)下必須要有一個(gè)線程來(lái)執(zhí)行任務(wù)。

runWorker()

簡(jiǎn)單說(shuō)明一下runWorker方法的執(zhí)行過(guò)程:

1.while循環(huán)不斷的通過(guò)getTask()方法獲取任務(wù);

2.然后運(yùn)行任何任務(wù)之前瓶蝴,拿到鎖來(lái)防止執(zhí)行任務(wù)時(shí)中斷毒返;

3.然后確保除非線程池停止,那么要保證當(dāng)前線程是中斷狀態(tài)舷手,否則保證當(dāng)前線程不是中斷狀態(tài)拧簸;

4.task.run()執(zhí)行任務(wù);

5.當(dāng)task為null的時(shí)候則跳出循環(huán)男窟,執(zhí)行processWorkerExit()方法盆赤;

6.runWorker方法執(zhí)行完畢,也代表著Worker中的run方法執(zhí)行完畢歉眷,銷毀線程牺六。

PS:beforeExecute方法和afterExecute方法在ThreadPoolExecutor類中是空的,留給子類實(shí)現(xiàn)姥芥。

completedAbruptly變量用來(lái)表示執(zhí)行任務(wù)的過(guò)程中是否出現(xiàn)了異常兔乞,processWorkerExit方法會(huì)對(duì)這個(gè)變量值進(jìn)行判斷。

線程池的合理配置

線程池需要合理的配置凉唐,首先應(yīng)該先分析任務(wù)的特點(diǎn)庸追,比如:

1.任務(wù)的性質(zhì):CPU密集型任務(wù),IO密集型任務(wù)還是混合型任務(wù)台囱;

2.任務(wù)的優(yōu)先級(jí):高淡溯,中,低簿训。

3.任務(wù)的執(zhí)行時(shí)間:長(zhǎng)咱娶,中,短强品。

IO密集型:由于需要等待IO操作膘侮,線程并不是一直在執(zhí)行任務(wù),可以 配置多一點(diǎn)線程的榛。

CPU密集型:CPU密集型任務(wù)需要大量的計(jì)算琼了,并且沒(méi)有什么阻塞,CPU一直全速運(yùn)行夫晌,所以盡可能少的配置線程數(shù)量雕薪。

混合型:混合型任務(wù)如果可以拆分,則拆分成CPU密集型和IO密集型任務(wù)晓淀,只要兩個(gè)任務(wù)執(zhí)行的時(shí)間相差不是特別大所袁,拆分后執(zhí)行的吞吐率應(yīng)該高于串行執(zhí)行的吞吐率。反之如果任務(wù)執(zhí)行時(shí)間相差太大凶掰,就沒(méi)必要拆分燥爷。

優(yōu)先級(jí):優(yōu)先級(jí)不同的任務(wù)可以使用優(yōu)先級(jí)隊(duì)列PriorityBlockQueue來(lái)處理蜈亩,它可以讓優(yōu)先級(jí)高的任務(wù)先執(zhí)行(如果一直有優(yōu)先級(jí)高的任務(wù)提交到隊(duì)列里,可能會(huì)造成優(yōu)先級(jí)低的任務(wù)永遠(yuǎn)得不到執(zhí)行)局劲。

執(zhí)行時(shí)間:執(zhí)行時(shí)間不同的任務(wù)也可以使用優(yōu)先級(jí)隊(duì)列勺拣,讓執(zhí)行時(shí)間短的任務(wù)先執(zhí)行。

?線程池的監(jiān)控

線程池提供了一些屬性可以進(jìn)行監(jiān)控鱼填,這樣有利于我們更好的使用線程池药有。

taskCount:線程池需要執(zhí)行的任務(wù)數(shù)量;

completedTaskCount:線程池在運(yùn)行過(guò)程中已經(jīng)完成的任務(wù)數(shù)量苹丸。

largestPoolSize:線程池曾經(jīng)創(chuàng)建過(guò)的最大線程數(shù)量愤惰。通過(guò)這個(gè)我們可以知道線程池是否滿過(guò)。如果等于線程池的最大大小赘理,則表示線程池曾經(jīng)滿過(guò)宦言。

getPoolSize:線程池的數(shù)量。

getActiveCount:活動(dòng)的線程數(shù)商模。

通過(guò)繼承線程池并且重寫(xiě)線程池的beforeExecute奠旺, afterExecute 和terminated方法,就可以在任務(wù)執(zhí)行前施流,執(zhí)行后和線程池關(guān)閉前做一些事情响疚。比如監(jiān)控任務(wù)的最大執(zhí)行時(shí)間,最小執(zhí)行時(shí)間等瞪醋。這幾個(gè)方法在線程池里都是空方法忿晕。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市银受,隨后出現(xiàn)的幾起案子践盼,更是在濱河造成了極大的恐慌,老刑警劉巖宾巍,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件咕幻,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡顶霞,警方通過(guò)查閱死者的電腦和手機(jī)谅河,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)确丢,“玉大人,你說(shuō)我怎么就攤上這事吐限∠式模” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵诸典,是天一觀的道長(zhǎng)描函。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么舀寓? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任胆数,我火速辦了婚禮,結(jié)果婚禮上互墓,老公的妹妹穿的比我還像新娘必尼。我一直安慰自己,他們只是感情好篡撵,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開(kāi)白布判莉。 她就那樣靜靜地躺著,像睡著了一般育谬。 火紅的嫁衣襯著肌膚如雪券盅。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 52,268評(píng)論 1 309
  • 那天膛檀,我揣著相機(jī)與錄音锰镀,去河邊找鬼。 笑死咖刃,一個(gè)胖子當(dāng)著我的面吹牛泳炉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播僵缺,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼胡桃,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了磕潮?” 一聲冷哼從身側(cè)響起翠胰,我...
    開(kāi)封第一講書(shū)人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎自脯,沒(méi)想到半個(gè)月后之景,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡膏潮,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年锻狗,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片焕参。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡轻纪,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出叠纷,到底是詐尸還是另有隱情刻帚,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布涩嚣,位于F島的核電站崇众,受9級(jí)特大地震影響掂僵,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜顷歌,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一锰蓬、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧眯漩,春花似錦芹扭、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至摹芙,卻和暖如春灼狰,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背浮禾。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工交胚, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人盈电。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓蝴簇,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親匆帚。 傳聞我的和親對(duì)象是個(gè)殘疾皇子熬词,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359

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