大家好,我是小黑枣抱,一個在互聯(lián)網(wǎng)茍且偷生的農(nóng)民工。
池化
線程池是在計算機(jī)開發(fā)中常見的一種池化技術(shù)辆床,是為了提高資源的利用率佳晶,將一些資源重復(fù)利用,避免重復(fù)的構(gòu)建來提高效率讼载。類似字符串常量池轿秧,數(shù)據(jù)庫連接池,HttpClient連接池等咨堤,都是用的池化技術(shù)菇篡。
線程池
在沒有線程池概念之前,我們要使用線程必須先通過創(chuàng)建一個Thread類來完成線程的構(gòu)建一喘,并調(diào)用start()
方法開啟驱还,在線程執(zhí)行完會將線程銷毀嗜暴,而線程資源是很寶貴的,創(chuàng)建和銷毀線程會造成資源的浪費议蟆。而線程池是將創(chuàng)建的線程存儲到一個池中闷沥,在需要使用時從池中去拿,使用完之后再講線程歸還到池中咐容,下一次接著使用舆逃。
舉個栗子,好比我們?nèi)ャy行辦理業(yè)務(wù)時戳粒,銀行會有窗口為客戶辦理業(yè)務(wù)路狮,如果沒有線程池,就好比每次來一個客戶蔚约,銀行都打開一個窗口奄妨,辦理完業(yè)務(wù)之后將窗口關(guān)閉,這樣確實很浪費時間炊琉,所以銀行會默認(rèn)開幾個窗口展蒂,比如三個,等客戶來辦理業(yè)務(wù)苔咪;一個客戶辦理完锰悼,下一個客戶可以繼續(xù)在這個窗口辦理。
核心線程數(shù)
在線程池初始化時团赏,會指定創(chuàng)建核心線程的數(shù)量箕般,有任務(wù)提交給線程池時,先判斷是否有空閑線程舔清,如果有空閑線程丝里,則直接使用,如果沒有則看當(dāng)前線程池中的數(shù)量是不是小于核心線程數(shù)体谒,如果是則創(chuàng)建新的線程杯聚,如果已經(jīng)到達(dá)核心線程數(shù),則需要做下一步操作抒痒。
等待隊列
下一步操作就是要進(jìn)入等待隊列幌绍,等待隊列好比是去銀行辦業(yè)務(wù)時沒有空閑窗口,需要坐在大廳的座椅上排隊故响;線程池也是一樣傀广,如果沒有核心線程,則需要將任務(wù)放入等待隊列彩届,等待有空閑線程再執(zhí)行伪冰。
最大線程數(shù)
那我們都知道銀行有時候人特別多的時候,會增加窗口樟蠕,一般是當(dāng)大廳的座椅坐滿人時贮聂,窗口都很緊張?zhí)幚聿贿^來靠柑,這是會增加窗口;線程池也是一樣寂汇,如果等待的任務(wù)已經(jīng)放滿了等待隊列病往,并且核心線程都在繁忙,這時會查看線程池中的線程數(shù)量是否到達(dá)最大線程數(shù)骄瓣,如果沒有則會創(chuàng)建新的線程來繼續(xù)處理。
拒絕策略
那如果說耍攘,線程池中的線程已經(jīng)到達(dá)最大線程數(shù)并且都在繁忙榕栏,還有新的任務(wù)進(jìn)來,好比銀行已經(jīng)坐滿人了蕾各,窗口也都在忙扒磁,客戶都排到門口了,這時要是還有人要辦理式曲,應(yīng)該怎么處理呢妨托?銀行一般會讓這個人先回家,改天再來辦吝羞,或者如果是個大客戶兰伤,銀行可能會單獨帶去VIP辦公室辦理等等。線程池在這種情況下也有相應(yīng)的處理方式钧排,這種處理方式我們稱之為拒絕策略敦腔,如果會放任務(wù)在當(dāng)前線程執(zhí)行,或者直接將任務(wù)丟棄等等恨溜,在后面的章節(jié)中我會詳細(xì)給大家介紹符衔。
JDK中的線程池
在JDK中提供了相應(yīng)的API來創(chuàng)建線程池,這些API也是在我們最近講到過的JUC包中糟袁。
Executor
首先判族,線程池需要具備能夠執(zhí)行任務(wù)的能力,這個任務(wù)通過一個線程來處理项戴。而這個執(zhí)行任務(wù)的能力通過Executor
接口來約定形帮。
public interface Executor {
void execute(Runnable command);
}
ExecutorService
在某些場景我們需要知道任務(wù)執(zhí)行完之后的結(jié)果,拿到返回值肯尺,而Runnable
接口是沒有返回值的沃缘;以及一些可以關(guān)閉線程池中的線程,執(zhí)行線程中的任務(wù)的方法则吟,定義在ExecutorService
接口中槐臀。
ThreadPoolExecutor
ThreaPoolExecutor
則是對ExecutorService
的具體實現(xiàn)類,通過ThreaPoolExecutor
類可以創(chuàng)建出一個線程池氓仲,我們先來看一下代碼水慨。
ThreadPoolExecutor executor = new ThreadPoolExecutor(
// 核心線程數(shù) corePoolSize
3,
// 最大線程數(shù) maximumPoolSize
5,
// 空閑線程保留存活的時間和時間單位
10, TimeUnit.SECONDS,
// 等待隊列
new ArrayBlockingQueue<>(3),
// 創(chuàng)建線程的工廠
Executors.defaultThreadFactory(),
// 拒絕策略
new ThreadPoolExecutor.AbortPolicy()
);
從代碼我們可以看出得糜,創(chuàng)建一個線程池,需要指定我們上面說到的核心線程數(shù)晰洒,最大線程數(shù)朝抖,等待隊列,拒絕策略等谍珊,并且還要指定創(chuàng)建線程的工廠對象治宣。
當(dāng)然ThreadPoolExecutor也有其他的構(gòu)造方法,可以不顯式指定拒絕策略和工廠對象砌滞。
new ThreadPoolExecutor(3,5,10,TimeUnit.SECONDS,new ArrayBlockingQueue<>(3));
// 構(gòu)造方法
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
// 使用默認(rèn)的線程工廠和默認(rèn)的拒絕策略侮邀。
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
7個線程池參數(shù)
- corePoolSize:核心線程數(shù)
- maximumPoolSize:最大線程數(shù)
- keepAliveTime:空閑線程保持存活時間
- unit:空閑線程保持存活時間單位
- workQueue:等待隊列
- threadFactory:線程創(chuàng)建工廠
- RejectedExecutionHandler:拒絕策略
4種拒絕策略
這里我們說一下4中拒絕策略。接口RejectedExecutionHandler
定義了拒絕策略贝润,所有的拒絕策略都需要實現(xiàn)該接口绊茧。
public interface RejectedExecutionHandler {
void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
在ThreadPoolExecutor類中定義了4個拒絕策略的具體實現(xiàn)。
- AbortPolicy:拒絕處理打掘,拋出異常
- CallerRunsPolicy:由創(chuàng)建該線程的線程(main)執(zhí)行
- DiscardPolicy: 丟棄华畏,不拋出異常
- DiscardOldestPolicy:和最早創(chuàng)建的線程進(jìn)行競爭,不拋出異常
可通過如下方式進(jìn)行拒絕策略的創(chuàng)建尊蚁。
// 拒絕處理亡笑,拋出異常
new ThreadPoolExecutor.AbortPolicy();
// 由創(chuàng)建該線程的線程(main)執(zhí)行
new ThreadPoolExecutor.CallerRunsPolicy();
// 丟棄,不拋出異常
new ThreadPoolExecutor.DiscardPolicy();
// 和最早創(chuàng)建的線程進(jìn)行競爭枝誊,不拋出異常
new ThreadPoolExecutor.DiscardOldestPolicy();
4種線程池種類
那么具體我們在使用時應(yīng)該創(chuàng)建怎樣的線程池呢况芒?在JDK的Executors
工具類為我們提供了4種線程池的創(chuàng)建方式。
// 只有一個線程
Executors.newSingleThreadExecutor();
// 固定線程數(shù)
Executors.newFixedThreadPool(5);
// 可伸縮的
Executors.newCachedThreadPool();
// 可延遲執(zhí)行叶撒,使用優(yōu)先隊列DelayedWorkQueue
Executors.newScheduledThreadPool(3);
小結(jié)
好的绝骚,通過今天的內(nèi)容我們先對線程池的使用有一個初步的了解,下期內(nèi)容再跟大家深入解析一下線程池中的具體實現(xiàn)原理祠够。
本期內(nèi)容就到這里压汪,我們下期見。