一:線程池參數(shù)簡介
ThreadPoolExecutor類可設(shè)置的參數(shù)主要有:
corePoolSize:核心線程
1.核心線程會(huì)一直存活甘苍,及時(shí)沒有任務(wù)需要執(zhí)行
2.當(dāng)線程數(shù)小于核心線程數(shù)時(shí)间涵,即使有線程空閑合敦,線程池也會(huì)優(yōu)先創(chuàng)建新線程處理
3.設(shè)置allowCoreThreadTimeout=true(默認(rèn)false)時(shí),核心線程會(huì)超時(shí)關(guān)閉
queueCapacity:任務(wù)隊(duì)列容量(阻塞隊(duì)列)
當(dāng)核心線程數(shù)達(dá)到最大時(shí),新任務(wù)會(huì)放在隊(duì)列中排隊(duì)等待執(zhí)行
maxPoolSize:最大線程數(shù)
1.當(dāng)線程數(shù)>=corePoolSize坐漏,且任務(wù)隊(duì)列已滿時(shí)。線程池會(huì)創(chuàng)建新線程來處理任務(wù)
2.當(dāng)線程數(shù)=maxPoolSize,且任務(wù)隊(duì)列已滿時(shí)赊琳,線程池會(huì)拒絕處理任務(wù)而拋出異常
keepAliveTime:線程空閑時(shí)間
1.當(dāng)線程空閑時(shí)間達(dá)到keepAliveTime時(shí)街夭,線程會(huì)退出,直到線程數(shù)量=corePoolSize
2.如果allowCoreThreadTimeout=true躏筏,則會(huì)直到線程數(shù)量=0
rejectedExecutionHandler:任務(wù)拒絕處理器
兩種情況會(huì)拒絕處理任務(wù):
1.當(dāng)線程數(shù)已經(jīng)達(dá)到maxPoolSize板丽,切隊(duì)列已滿,會(huì)拒絕新任務(wù)
2.當(dāng)線程池被調(diào)用shutdown()后趁尼,會(huì)等待線程池里的任務(wù)執(zhí)行完畢埃碱,再shutdown。如果在調(diào)用shutdown()和線程池真正shutdown之間提交任務(wù)酥泞,會(huì)拒絕新任務(wù)砚殿。
線程池會(huì)調(diào)用rejectedExecutionHandler來處理這個(gè)任務(wù)。如果沒有設(shè)置芝囤,默認(rèn)是AbortPolicy似炎,會(huì)拋出異常。
ThreadPoolExecutor類有幾個(gè)內(nèi)部實(shí)現(xiàn)類來處理拒絕任務(wù):
1.AbortPolicy 丟棄任務(wù)悯姊,拋運(yùn)行時(shí)異常
2.CallerRunsPolicy 執(zhí)行任務(wù)
3.DiscardPolicy 忽視羡藐,什么都不會(huì)發(fā)生
4.DiscardOldestPolicy 從隊(duì)列中踢出最先進(jìn)入隊(duì)列(最后一個(gè)執(zhí)行)的任務(wù)
5.實(shí)現(xiàn)RejectedExecutionHandler接口,可自定義處理器
二:ThreadPoolExecutor執(zhí)行順序:
1.當(dāng)線程數(shù)小于核心線程數(shù)時(shí)悯许,創(chuàng)建線程传睹。
2.當(dāng)線程數(shù)大于等于核心線程數(shù),且任務(wù)隊(duì)列未滿時(shí)岸晦,將任務(wù)放入任務(wù)隊(duì)列欧啤。
3.當(dāng)線程數(shù)大于等于核心線程數(shù),且任務(wù)隊(duì)列已滿
3.1若線程數(shù)小于最大線程數(shù)启上,創(chuàng)建線程
3.2若線程數(shù)等于最大線程數(shù)邢隧,拋出異常,拒絕任務(wù)
三:線程池參數(shù)的合理設(shè)置
為了說明合理設(shè)置的條件冈在,我們首先確定有以下幾個(gè)相關(guān)參數(shù):
1.tasks倒慧,程序每秒需要處理的最大任務(wù)數(shù)量(假設(shè)系統(tǒng)每秒任務(wù)數(shù)為100~1000)
2.tasktime,單線程處理一個(gè)任務(wù)所需要的時(shí)間(每個(gè)任務(wù)耗時(shí)0.1秒)
3.responsetime包券,系統(tǒng)允許任務(wù)最大的響應(yīng)時(shí)間(每個(gè)任務(wù)的響應(yīng)時(shí)間不得超過2秒)
corePoolSize
每個(gè)任務(wù)需要tasktime秒處理纫谅,則每個(gè)線程每秒可處理1/tasktime個(gè)任務(wù)。系統(tǒng)每秒有tasks個(gè)任務(wù)需要處理溅固,則需要的線程數(shù)為:tasks/(1/tasktime)付秕。
即tasks*tasktime個(gè)線程數(shù)。假設(shè)系統(tǒng)每秒任務(wù)數(shù)為100到1000之間侍郭,每個(gè)任務(wù)耗時(shí)0.1秒询吴,則需要100x0.1至1000x0.1掠河,即10到100個(gè)線程。那么corePoolSize應(yīng)該設(shè)置為大于10猛计。
具體數(shù)字最好根據(jù)8020原則唠摹,即80%情況下系統(tǒng)每秒任務(wù)數(shù),若系統(tǒng)80%的情況下任務(wù)數(shù)小于200奉瘤,最多時(shí)為1000勾拉,則corePoolSize可設(shè)置為20。
queueCapacity:任務(wù)隊(duì)列的長度
任務(wù)隊(duì)列的長度要根據(jù)核心線程數(shù)盗温,以及系統(tǒng)對(duì)任務(wù)響應(yīng)時(shí)間的要求有關(guān)望艺。隊(duì)列長度可以設(shè)置為(corePoolSize/tasktime)responsetime: (20/0.1)2=400,即隊(duì)列長度可設(shè)置為400肌访。
如果隊(duì)列長度設(shè)置過大找默,會(huì)導(dǎo)致任務(wù)響應(yīng)時(shí)間過長,如以下寫法:
LinkedBlockingQueue queue = new LinkedBlockingQueue();
這實(shí)際上是將隊(duì)列長度設(shè)置為Integer.MAX_VALUE吼驶,將會(huì)導(dǎo)致線程數(shù)量永遠(yuǎn)為corePoolSize惩激,再也不會(huì)增加,當(dāng)任務(wù)數(shù)量陡增時(shí)蟹演,任務(wù)響應(yīng)時(shí)間也將隨之陡增风钻。
maxPoolSize:最大線程數(shù)
當(dāng)系統(tǒng)負(fù)載達(dá)到最大值時(shí),核心線程數(shù)已無法按時(shí)處理完所有任務(wù)酒请,這時(shí)就需要增加線程骡技。每秒200個(gè)任務(wù)需要20個(gè)線程,那么當(dāng)每秒達(dá)到1000個(gè)任務(wù)時(shí)羞反,則需要(1000-queueCapacity)*(20/200)布朦,即60個(gè)線程,可將maxPoolSize設(shè)置為60昼窗。
keepAliveTime:
線程數(shù)量只增加不減少也不行是趴。當(dāng)負(fù)載降低時(shí),可減少線程數(shù)量澄惊,如果一個(gè)線程空閑時(shí)間達(dá)到keepAliveTiime唆途,該線程就退出。默認(rèn)情況下線程池最少會(huì)保持corePoolSize個(gè)線程掸驱。keepAliveTiime設(shè)定值可根據(jù)任務(wù)峰值持續(xù)時(shí)間來設(shè)定肛搬。
以上關(guān)于線程數(shù)量的計(jì)算并沒有考慮CPU的情況。若結(jié)合CPU的情況毕贼,比如温赔,當(dāng)線程數(shù)量達(dá)到50時(shí),CPU達(dá)到100%帅刀,則將maxPoolSize設(shè)置為60也不合適让腹,此時(shí)若系統(tǒng)負(fù)載長時(shí)間維持在每秒1000個(gè)任務(wù),則超出線程池處理能力扣溺,應(yīng)設(shè)法降低每個(gè)任務(wù)的處理時(shí)間(tasktime)。
ps:
該文章創(chuàng)作原因來源于阿里java后端面試:
如果給你8G內(nèi)存,500G固態(tài)硬盤譬圣,雙CPU四核的配置源葫,現(xiàn)在有100個(gè)用戶訪問你的系統(tǒng),請(qǐng)你設(shè)計(jì)一下你剛剛說的那些線程池參數(shù)驱犹。