關(guān)于線程池你不得不知道的一些設(shè)置
看完我上一篇文章,「你都理解創(chuàng)建線程池的參數(shù)嗎?」之后,當(dāng)遇到這種問題,你覺得你完全能夠唬住面試官了,50k輕松到手。殊不知,要是面試官此刻給你來個反殺:
初始化線程池時可以預(yù)先創(chuàng)建線程嗎?線程池的核心線程可以被回收嗎?為什么?
如果此刻你一臉懵逼,這個要慌,問題很大,50k馬上變5k。
有細(xì)心的網(wǎng)友早就想到了這個問題:

在ThreadPoolExecutor線程池中,還有一些不常用的設(shè)置。我建議如果您在應(yīng)用場景中沒有特殊的要求,就不需要使用這些設(shè)置。
初始化線程池時可以預(yù)先創(chuàng)建線程嗎?
prestartAllCoreThreads
初始化線程池時是可以預(yù)先創(chuàng)建線程的,初始化線程池后,再調(diào)用prestartAllCoreThreads()方法,即可預(yù)先創(chuàng)建corePoolSize數(shù)量的核心線程,我們看源碼:
public int prestartAllCoreThreads() {
int n = 0;
while (addWorker(null, true))
++n;
return n;
}
private boolean addWorker(Runnable firstTask, boolean core) {
// ..
}addWorker方法目的是在線程池中添加任務(wù)并執(zhí)行,如果task為空,線程獲取任務(wù)執(zhí)行時調(diào)用getTask()方法,該方法從blockingQueue阻塞隊列中阻塞獲取任務(wù)執(zhí)行,因此線程不會釋放,留存在線程池中,如果core=true,說明任務(wù)只能利用核心線程來執(zhí)行。
所以該方法會在線程池總預(yù)先創(chuàng)建沒有任務(wù)執(zhí)行的線程,數(shù)量為corePoolSize。
下面我們測試一下:

從測試結(jié)果來看,線程池中已經(jīng)預(yù)先創(chuàng)建了corePoolSize數(shù)量的空閑線程。
prestartCoreThread
prestartCoreThread()同樣可以預(yù)先創(chuàng)建線程,只不過該方法只會與創(chuàng)建1條線程,我們來看源碼:
public boolean prestartCoreThread() {
return workerCountOf(ctl.get()) < corePoolSize &&
addWorker(null, true);
}從方法源碼可知,如果此時工作線程數(shù)量小于corePoolSize,那么就調(diào)用addWorker創(chuàng)建1條空閑核心線程。
下面我們測試一下:

從測試結(jié)果來看,線程池中已經(jīng)預(yù)先創(chuàng)建了1條空閑線程。
線程池的核心線程可以被回收嗎?
你可能會想到將corePoolSize的數(shù)量設(shè)置為0,從而線程池的所有線程都是“臨時”的,只有keepAliveTime存活時間,你的思路也許時正確的,但你有沒有想過一個很嚴(yán)重的后果,corePoolSize=0時,任務(wù)需要填滿阻塞隊列才會創(chuàng)建線程來執(zhí)行任務(wù),阻塞隊列有設(shè)置長度還好,如果隊列長度無限大呢,你就等著OOM異常吧,所以用這種設(shè)置行為并不是我們所需要的。
有沒有什么設(shè)置可以回收核心線程呢?
allowCoreThreadTimeOut
ThreadPoolExecutor有一個私有成員變量:
private volatile boolean allowCoreThreadTimeOut;
如果allowCoreThreadTimeOut=true,核心線程在規(guī)定時間內(nèi)會被回收。
上面我也說了,當(dāng)線程空閑時會從blockingQueue阻塞隊列中阻塞獲取任務(wù)執(zhí)行,所以我們來看看是保證核心線程不被銷毀的,我們直接定位到源碼部位:
java.util.concurrent.ThreadPoolExecutor#getTask:
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
// Are workers subject to culling?
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
這里的關(guān)鍵值timed,如果allowCoreThreadTimeOut=true或者此時工作線程大于corePoolSize,timed=true,如果timed=true,會調(diào)用poll()方法從阻塞隊列中獲取任務(wù),否則調(diào)用take()方法獲取任務(wù)。
下面我來解釋這兩個方法:
- poll(long timeout, TimeUnit unit):從BlockingQueue取出一個任務(wù),如果不能立即取出,則可以等待timeout參數(shù)的時間,如果超過這個時間還不能取出任務(wù),則返回null;
- take():從blocking阻塞隊列取出一個任務(wù),如果BlockingQueue為空,阻斷進(jìn)入等待狀態(tài)直到BlockingQueue有新的任務(wù)被加入為止。
到這里,我們就很好地解釋了,當(dāng)allowCoreThreadTimeOut=true或者此時工作線程大于corePoolSize時,線程調(diào)用BlockingQueue的poll方法獲取任務(wù),若超過keepAliveTime時間,則返回null,timedOut=true,則getTask會返回null,線程中的runWorker方法會退出while循環(huán),線程接下來會被回收。
下面我們測試一下:

以上所述是小編給大家介紹的java線程池設(shè)置詳解整合,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!
相關(guān)文章
SpringBoot從yml配置文件中讀常用參數(shù)值實例方法
在本篇文章里小編給大家整理了關(guān)于SpringBoot從yml配置文件中讀常用參數(shù)值實例方法,有需要的朋友們學(xué)習(xí)下。2019-12-12
SpringCloud Alibaba使用Seata處理分布式事務(wù)的技巧
在傳統(tǒng)的單體項目中,我們使用@Transactional注解就能實現(xiàn)基本的ACID事務(wù)了,隨著微服務(wù)架構(gòu)的引入,需要對數(shù)據(jù)庫進(jìn)行分庫分表,每個服務(wù)擁有自己的數(shù)據(jù)庫,這樣傳統(tǒng)的事務(wù)就不起作用了,那么我們?nèi)绾伪WC多個服務(wù)中數(shù)據(jù)的一致性呢?跟隨小編一起通過本文了解下吧2021-06-06
SpringBoot集成slf4j2日志配置的實現(xiàn)示例
本文主要介紹了SpringBoot集成slf4j2日志配置的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-08-08

