為什么要使用線程池赖草?
引用自?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.
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è)方法在線程池里都是空方法忿晕。