線程池

引言

合理利用線程池能夠帶來(lái)三個(gè)好處。第一:降低資源消耗。通過(guò)重復(fù)利用已創(chuàng)建的線程降低線程創(chuàng)建和銷毀造成的消耗。第二:提高響應(yīng)速度。當(dāng)任務(wù)到達(dá)時(shí),任務(wù)可以不需要的等到線程創(chuàng)建就能立即執(zhí)行。第三:提高線程的可管理性。線程是稀缺資源,如果無(wú)限制的創(chuàng)建,不僅會(huì)消耗系統(tǒng)資源,還會(huì)降低系統(tǒng)的穩(wěn)定性,使用線程池可以進(jìn)行統(tǒng)一的分配,調(diào)優(yōu)和監(jiān)控。但是要做到合理的利用線程池,必須對(duì)其原理了如指掌。

線程池的使用

線程池的創(chuàng)建

創(chuàng)建一個(gè)線程池需要輸入幾個(gè)參數(shù):

corePoolSize(線程池的基本大小):當(dāng)提交一個(gè)任務(wù)到線程池時(shí),線程池會(huì)創(chuàng)建一個(gè)線程來(lái)執(zhí)行任務(wù),即使其他基本線程是空閑的,直到需要執(zhí)行的任務(wù)數(shù)大于線程池基本大小時(shí)就不再創(chuàng)建。如果調(diào)用了線程池的prestartAllCoreThreads方法,線程池會(huì)提前啟動(dòng)所有基本線程。

runnableTaskQueue(任務(wù)隊(duì)列):用于保存等待執(zhí)行的任務(wù)的阻塞隊(duì)列。

可以選擇以下幾個(gè)阻塞隊(duì)列。

ArrayBlockingQueue:是一個(gè)基于數(shù)組結(jié)構(gòu)的有界阻塞隊(duì)列,此隊(duì)列按 FIFO(先進(jìn)先出)原則對(duì)元素進(jìn)行排序。

LinkedBlockingQueue:一個(gè)基于鏈表結(jié)構(gòu)的阻塞隊(duì)列,此隊(duì)列按FIFO (先進(jìn)先出)排序元素,吞吐量通常要高于ArrayBlockingQueue。靜態(tài)工廠方法Executors.newFixedThreadPool()使用了這個(gè)隊(duì)列。

SynchronousQueue:一個(gè)不存儲(chǔ)元素的阻塞隊(duì)列。每個(gè)插入操作必須等到另一個(gè)線程調(diào)用移除操作,否則插入操作一直處于阻塞狀態(tài),吞吐量通常要高于LinkedBlockingQueue,靜態(tài)工廠方法Executors.newCachedThreadPool使用了這個(gè)隊(duì)列。

PriorityBlockingQueue:一個(gè)具有優(yōu)先級(jí)得無(wú)限阻塞隊(duì)列。

maximumPoolSize(線程池最大大小):線程池允許創(chuàng)建的最大線程數(shù)。如果隊(duì)列滿了,并且已創(chuàng)建的線程數(shù)小于最大線程數(shù),則線程池會(huì)再創(chuàng)建新的線程執(zhí)行任務(wù)。值得注意的是如果使用了無(wú)界的任務(wù)隊(duì)列這個(gè)參數(shù)就沒(méi)什么效果。

ThreadFactory:用于設(shè)置創(chuàng)建線程的工廠,可以通過(guò)線程工廠給每個(gè)創(chuàng)建出來(lái)的線程設(shè)置更有意義的名字。

RejectedExecutionHandler(飽和策略):當(dāng)隊(duì)列和線程池都滿了,說(shuō)明線程池處于飽和狀態(tài),那么必須采取一種策略處理提交的新任務(wù)。這個(gè)策略默認(rèn)情況下是AbortPolicy,表示無(wú)法處理新任務(wù)時(shí)拋出異常。以下是JDK1.5提供的四種策略。

AbortPolicy:直接拋出異常。

CallerRunsPolicy:只用調(diào)用者所在線程來(lái)運(yùn)行任務(wù)。

DiscardOldestPolicy:丟棄隊(duì)列里最近的一個(gè)任務(wù),并執(zhí)行當(dāng)前任務(wù)。

DiscardPolicy:不處理,丟棄掉。

當(dāng)然也可以根據(jù)應(yīng)用場(chǎng)景需要來(lái)實(shí)現(xiàn)RejectedExecutionHandler接口自定義策略。如記錄日志或持久化不能處理的任務(wù)。

keepAliveTime(線程活動(dòng)保持時(shí)間):線程池的工作線程空閑后,保持存活的時(shí)間。所以如果任務(wù)很多,并且每個(gè)任務(wù)執(zhí)行的時(shí)間比較短,可以調(diào)大這個(gè)時(shí)間,提高線程的利用率。

TimeUnit(線程活動(dòng)保持時(shí)間的單位):可選的單位有天(DAYS),小時(shí)(HOURS),分鐘(MINUTES),毫秒(MILLISECONDS),微秒(MICROSECONDS,千分之一毫秒)和毫微秒(NANOSECONDS,千分之一微秒)。



線程池的分析

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ù),滿了,則交給飽和策略來(lái)處理這個(gè)任務(wù)。



合理的配置線程池

要想合理的配置線程池,就必須首先分析任務(wù)特性,可以從以下幾個(gè)角度來(lái)進(jìn)行分析:

1.?任務(wù)的性質(zhì):CPU密集型任務(wù),IO密集型任務(wù)和混合型任務(wù)。

2.?任務(wù)的優(yōu)先級(jí):高,中和低。

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

4.?任務(wù)的依賴性:是否依賴其他系統(tǒng)資源,如數(shù)據(jù)庫(kù)連接。

任務(wù)性質(zhì)不同的任務(wù)可以用不同規(guī)模的線程池分開處理。CPU密集型任務(wù)配置盡可能小的線程,如配置Ncpu 1個(gè)線程的線程池。IO密集型任務(wù)則由于線程并不是一直在執(zhí)行任務(wù),則配置盡可能多的線程,如2*Ncpu。混合型的任務(wù),如果可以拆分,則將其拆分成一個(gè)CPU密集型任務(wù)和一個(gè)IO密集型任務(wù),只要這兩個(gè)任務(wù)執(zhí)行的時(shí)間相差不是太大,那么分解后執(zhí)行的吞吐率要高于串行執(zhí)行的吞吐率,如果這兩個(gè)任務(wù)執(zhí)行時(shí)間相差太大,則沒(méi)必要進(jìn)行分解。使用Runtime.getRuntime().availableProcessors()得到CPU的個(gè)數(shù)。

優(yōu)先級(jí)不同的任務(wù)可以使用優(yōu)先級(jí)隊(duì)列PriorityBlockingQueue來(lái)處理。它可以讓優(yōu)先級(jí)高的任務(wù)先得到執(zhí)行,需要注意的是如果一直有優(yōu)先級(jí)高的任務(wù)提交到隊(duì)列里,那么優(yōu)先級(jí)高的任務(wù)可能永遠(yuǎn)不能執(zhí)行。

執(zhí)行時(shí)間不同的任務(wù)可以交給不同規(guī)模的線程池來(lái)處理,或者也可以使用優(yōu)先級(jí)隊(duì)列,讓執(zhí)行時(shí)間短的任務(wù)先執(zhí)行。

依賴數(shù)據(jù)庫(kù)連接池的任務(wù),因?yàn)榫€程提交SQL后,需要等待數(shù)據(jù)庫(kù)返回結(jié)果,如果等待的時(shí)間越長(zhǎng),CPU空閑時(shí)間就越長(zhǎng),那么線程數(shù)應(yīng)該設(shè)置越大,這樣才能更好的利用CPU。

配置線程池有兩點(diǎn)建議:第一,使用變量(如CPU的個(gè)數(shù),連接池的大小)來(lái)動(dòng)態(tài)配置線程池大小,這樣做可以增加線程池的可伸縮性,即當(dāng)CPU增加時(shí),線程池的處理能力相應(yīng)增加。第二,使用有界隊(duì)列。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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