前言
如果有人問我:“你了解Java線程池嗎”,我不打算回答Java中常用的幾種線程池僚焦,也記不住。從線程池的上層API來看曙痘,再多種的線程池芳悲,無非是參數(shù)的不同立肘,讓它們呈現(xiàn)出了不同的特性,那么這些特性到底依賴什么樣的原理實現(xiàn)名扛,就更值得去深究谅年,也是本文的目的。
試著回答以下幾個問題:
- 線程池如何實現(xiàn)
- 非核心線程延遲死亡肮韧,如何做到
- 核心線程為什么不會死
- 如何釋放核心線程
- 非核心線程能成為核心線程嗎
- Runnable在線程池里如何執(zhí)行
- 線程數(shù)如何做選擇
- 常見的不同類型的線程池的功效如何做到
如果以上問題回答不出一二三融蹂,可以借鑒本文。
基礎知識
要了解線程池弄企,必然涉及到ThreadPoolExecutor超燃。ThreadPoolExecutors實現(xiàn)了線程池所需的最小功能集,已能hold住很多場景拘领。常見的線程池類型意乓,通過Executors提供的API,屏蔽了構造參數(shù)細節(jié)來創(chuàng)建ThreadPoolExecutors院究,因為不了解具體參數(shù)含義的話洽瞬,可能拿到的線程池與設想的會有偏差。
構造參數(shù)與對象成員變量
- corePoolSize:核心線程數(shù)业汰,期望保持的并發(fā)狀態(tài)
- maximumPoolSize:最大線程數(shù)伙窃,允許超載,雖然期望將并發(fā)狀態(tài)保持在一定范圍样漆,但是在任務過多時为障,增加非核心線程來處理任務。非核心線程數(shù) = maximumPoolSize - corePoolSize
- workQueue:阻塞隊列放祟,存儲線程任務Runnable
- keepAliveTime:在沒有任務時鳍怨,線程存活時間
- threadFactory:用來構建線程
- handler:當任務已滿,并且無法再增加線程數(shù)時跪妥,或拒絕添加任務時侦香,所執(zhí)行的策略
Worker
線程池中的工作線程以Worker作為體現(xiàn),真正工作的線程為Worker的成員變量顷锰,Worker即是Runnable,又是同步器贫堰。Worker從工作隊列中取出任務來執(zhí)行熄云,并能通過Worker控制任務狀態(tài)。
ctl
ctl用來控制線程池的狀態(tài)晴及,并用來表示線程池線程數(shù)量及皂。在線程池中,有以下五種狀態(tài)
- RUNNABLE:運行狀態(tài)且改,接受新任務验烧,持續(xù)處理任務隊列里的任務
- SHUTDOWN:不再接受新任務又跛,但要處理任務隊列里的任務
- STOP:不接受新任務慨蓝,不再處理任務隊列里的任務,中斷正在進行中的任務
- TIDYING:表示線程池正在停止運作礼烈,中止所有任務弧满,銷毀所有工作線程
- TERMINATED:表示線程池已停止運作,所有工作線程已被銷毀济丘,所有任務已被清空或執(zhí)行完畢
狀態(tài)轉(zhuǎn)換關系如下圖
ctl類型為AtomicInteger谱秽,那用一個基礎如何表示以上五種狀態(tài)以及線程池工作線程數(shù)量呢?int型變量占用4字節(jié)摹迷,共32位疟赊,因此采用位表示,可以解決上述問題峡碉。5種狀態(tài)使用5種數(shù)值進行表示近哟,需要占用3位,余下的29位就可以用來表示線程數(shù)鲫寄。因此吉执,高三位表示進程狀態(tài),低29位為線程數(shù)量地来,代碼如下:
// 值為29
private static final int COUNT_BITS = Integer.SIZE - 3;
// 高三位全為0戳玫,低29位全為1,因此線程數(shù)量的表示范圍為 0 ~ 2^29
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
/**
因為ctl分位來表示狀態(tài)和數(shù)量未斑,下面幾個狀態(tài)僅看有效位的值
*/
// 有效值為 111
private static final int RUNNING = -1 << COUNT_BITS;
// 有效值為 000
private static final int SHUTDOWN = 0 << COUNT_BITS;
// 有效值為 001
private static final int STOP = 1 << COUNT_BITS;
// 有效值為 010
private static final int TIDYING = 2 << COUNT_BITS;
// 有效值為 011
private static final int TERMINATED = 3 << COUNT_BITS;
// 默認狀態(tài)為RUNNING咕宿,線程數(shù)量為0
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
既然采用了int分位表示線程池狀態(tài)和線程數(shù)量,那么線程池自然提供了方法來獲取狀態(tài)與數(shù)量
- runStateOf(): 獲取線程池狀態(tài)
- workerCountOf(): 獲取工作線程數(shù)量
兩函數(shù)均為二進制操作,代碼不貼府阀,可用下圖說明:
線程池實現(xiàn)
添加任務
線程池可以通過submit()缆镣、execute()提交線程任務,其中试浙,submit()可以通過Future拿到執(zhí)行結果董瞻,內(nèi)部也是通過execute()向線程池提交線程任務.
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
// 獲取當前ctl值
int c = ctl.get();
// 當前線程數(shù)少于最大核心線程數(shù)
if (workerCountOf(c) < corePoolSize) {
// 添加核心線程,添加線程任務
if (addWorker(command, true))
return;
// 上面的過程期間田巴,ctl可能已被更改钠糊,獲取最新值
c = ctl.get();
}
// 線程池狀態(tài)為RUNNABLE,向工作隊列添加任務
if (isRunning(c) && workQueue.offer(command)) {
// 再次檢查用
int recheck = ctl.get();
// 線程不處于RUNNABLE狀態(tài)固额,移除任務
if (! isRunning(recheck) && remove(command))
// 執(zhí)行拒絕任務策略
reject(command);
else if (workerCountOf(recheck) == 0)
// 執(zhí)行到這里說明已沒有可用的工作線程眠蚂,創(chuàng)建新的工作現(xiàn)線程
// ,并從任務隊列里取任務斗躏。因為在這個時刻逝慧,存在所有工作線程
// 都被釋放的可能,為了應對這個線程池“假死”的情況啄糙,所以創(chuàng)建
// 了新的工作線程
addWorker(null, false);
}
// 添加非核心隊列來執(zhí)行線程任務
else if (!addWorker(command, false))
// 說明線程池達到飽和笛臣,或者線程池shut down,執(zhí)行拒絕策略
reject(command);
}
當有任務到來時隧饼,按照如下策略進行:
- 如果當前核心線程數(shù)量沒達到最大值corePoolSize沈堡,創(chuàng)建新線程來執(zhí)行此任務
- 如果當前核心線程到達最大,向阻塞隊列添加任務
- 如果核心線程已滿燕雁,阻塞隊列已滿诞丽,嘗試開啟非核心線程來執(zhí)行任務
- 如果線程池不處于RUNNABLE狀態(tài),或者處于飽和狀態(tài)拐格,執(zhí)行任務拒絕策略
線程池是按照上面123的順序來處理新進的任務的僧免,并且在每一個過程中,會檢查ctl的最新值有效性捏浊,因為在處理過程中線程池的各種狀態(tài)隨時可能發(fā)生了改變懂衩。
不過是通過添加核心或是通過添加非核心線程來執(zhí)行任務,都是通過addWorker()來完成金踪,下面是代碼
private boolean addWorker(Runnable firstTask, boolean core) {
// 這個是類似 goto 的語法浊洞,代碼有效片段是下面第一for循環(huán)
retry:
for (;;) {
int c = ctl.get();
// 獲取程序狀態(tài)
int rs = runStateOf(c);
/**
這一個條件需要仔細理解。
1. 當線程處于STOP胡岔、TIDYING法希、TERMINATED時,線程池是拒絕執(zhí)行任務的
因此不需要任務靶瘸,也不添加線程
2. 當線程處于SHUTDOWN狀態(tài)時铁材,線程池需要把任務處理完尖淘,才會到達后面的
TIDYING、TERMINATED狀態(tài)著觉。因此,如果阻塞隊列還有任務的話惊暴,繼續(xù)添加
線程來加快處理饼丘。
*/
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
// 獲取線程數(shù)
int wc = workerCountOf(c);
// 線程數(shù)超過或等于能表示的上限
// 或 比較 核心線程數(shù)達到上限,或比較線程池允許的最大線程數(shù)辽话,取決于core
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
// CAS操作增加線程數(shù)肄鸽,跳出循環(huán)
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
// 上面的CAS操作沒成功,檢查線程池狀態(tài)與開始是否一致油啤,
// 如果一致典徘,繼續(xù)執(zhí)行此for循環(huán),否則重新執(zhí)行retry代碼塊益咬,
// 自旋以期CAS成功逮诲,后續(xù)才能添加線程
if (runStateOf(c) != rs)
continue retry;
}
}
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
// 將線程任務加入Worker,新增了Worker幽告,就是新增了線程
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
int rs = runStateOf(ctl.get());
// 再次檢查線程池狀態(tài)
// 1. 處于RUNNABLE狀態(tài)梅鹦,繼續(xù)添加線程執(zhí)行任務
// 2. 處于SHUTDOWN狀態(tài),到這里說明隊列里還有任務要執(zhí)行
// 增加線程期望讓任務執(zhí)行快一點
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
// 這里說明發(fā)生了意外狀況冗锁,新建的線程不可用
if (t.isAlive())
throw new IllegalThreadStateException();
// 添加worker進集合
workers.add(w);
int s = workers.size();
// largestPoolSize可以表示線程池達到的最大并發(fā)
if (s > largestPoolSize)
largestPoolSize = s;
// 添加線程成功
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
// 啟動新添加的線程
t.start();
// 線程啟動成功
workerStarted = true;
}
}
} finally {
if (! workerStarted)
// 線程啟動失敗齐唆,移除work,銷毀線程
addWorkerFailed(w);
}
return workerStarted;
}
以上代碼做了如下幾件事:
- 線程池處于 RUNNBALE 或者處于 SHUTDOWN 并在阻塞隊列里還有任務時冻河,需要添加新線程箍邮。自旋確保 CAS 成功,然后添加新線程
- 線程存于Worker叨叙,線程池存有Worker信息锭弊,就能訪問線程
- 線程啟動失敗,則移除Worker摔敛,銷毀線程
addWorkerFailed()操作就不進去看了廷蓉,首先是將Worker移除,然后通過CAS操作更新ctl马昙,最后調(diào)用tryTerminate()操作嘗試中止線程池桃犬。
執(zhí)行任務
之前的代碼開啟了新線程并讓線程執(zhí)行,但是沒有看到有Runnable提交行楞。之前說過Worker本身為Runnable攒暇,并且存有為Thread類型的成員變量。線程池執(zhí)行的任務的線程子房,也就是Workder里的Thread形用。
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
Worker(Runnable firstTask) {
setState(-1);
// firstTask就是addWorker()帶來的Runnable
this.firstTask = firstTask;
// 通過ThreadFactory創(chuàng)建線程就轧,將自己作為Runnable提交
this.thread = getThreadFactory().newThread(this);
}
......
}
因此線程執(zhí)行后,執(zhí)行的是Worker.run()田度,run()則調(diào)用了ThreadPoolExecutor.runWorker()
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock();
boolean completedAbruptly = true;
try {
// task一開始是firstTask妒御, 后面就通過getTask()從阻塞隊列里拿任務
while (task != null || (task = getTask()) != null) {
w.lock();
// 線程池狀態(tài)檢查
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
// 這個方法子類可以重寫,在任務執(zhí)行前有回調(diào)
beforeExecute(wt, task);
Throwable thrown = null;
try {
// 執(zhí)行任務
task.run();
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
// 這個方法子類可以重寫镇饺,在任務執(zhí)行后有回調(diào)
afterExecute(task, thrown);
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
// 線程池已沒有任務了乎莉,工作線程達到了可退出的狀態(tài),Worker退出
processWorkerExit(w, completedAbruptly);
}
}
線程首個任務為firstTask奸笤,之后通過getTask()就從阻塞隊列里任務惋啃。線程池提供了beforeExecute()和afterExecute()通知子類任務執(zhí)行前后的回調(diào),讓子類有時機能執(zhí)行自己的事情监右。如果線程池已沒有任務了边灭,工作線程達到了可退出的狀態(tài),則將線程退出健盒。
主要看getTask() 和 processWorkerExit()
private Runnable getTask() {
// 超時標志
boolean timedOut = false;
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// 檢查線程池和阻塞隊列狀態(tài)
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
// 減少線程數(shù)
decrementWorkerCount();
return null;
}
// 獲取線程數(shù)
int wc = workerCountOf(c);
// 線程等待方式標志位判斷依據(jù)
// allowCoreThreadTimeOut代表核心線程是不是能退出绒瘦,如果核心線程能退出,就更別說非核心線程了
// 另一個則是看是否存在非核心線程
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
// 超時味榛,或者并且線程超標超標椭坚,返回null,讓上一層函數(shù)退出線程
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
// 如果timed為true搏色,則使用poll等待最多keepAliveTime時間獲取任務
// 如果timed為false善茎,使用take()獲取任務,阻塞線程频轿,直到可以從阻塞隊列拿到任務
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
// 超時
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
線程池里的線程從阻塞隊列里拿任務垂涯,如果存在非核心線程,假設阻塞隊列里沒有任務航邢,那么非核心線程也要在等到keepAliveTime時間后才會釋放耕赘。如果當前僅有核心線程存在,如果允許釋放核心線程的話膳殷,也就和非核線程的處理方式一樣操骡,反之,則通過take()一直阻塞直到拿到任務赚窃,這也就是線程池里的核心線程為什么不死的原因册招。
從之前的代碼一直看到這,并沒有發(fā)現(xiàn)有明顯的標志來標志核心線程與非核心線程勒极,而是以線程數(shù)來表達線程身份是掰。0 ~ corePoolSize 表示線程池里只有核心線程,corePoolSize ~ maximumPoolSize 表示線程池里核心線程滿辱匿,存在非核心線程键痛。然后炫彩,根據(jù)區(qū)間狀態(tài)做有差異的處理⌒醵蹋可以大膽猜測江兢,線程池實際并不區(qū)分核心線程與非核心線程,是根據(jù)當前的總體并發(fā)狀態(tài)來決定怎樣處理線程任務丁频。corePoolSize是線程池希望達到并保持的并發(fā)狀態(tài)划址,而corePoolSize ~ maximumPoolSize則是線程池允許的并發(fā)的超載狀態(tài),不希望長期保持限府。
釋放線程
在線程沒有拿到任務后,退出線程痢缎,通過processWorkerExit()可以證實上述所言胁勺。
private void processWorkerExit(Worker w, boolean completedAbruptly) {
// 到這里說明線程中斷,先通過decrementWorkerCount()減少線程數(shù)值
// 否則独旷,說明是線程沒有從阻塞隊列獲取到線程
if (completedAbruptly)
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// completedTaskCount記錄線程池總共完成的任務
// w.completedTasks則是線程完成的任務數(shù)
completedTaskCount += w.completedTasks;
// 移除Worker
workers.remove(w);
} finally {
mainLock.unlock();
}
// 線程池狀態(tài)改變署穗,嘗試中止線程池
tryTerminate();
int c = ctl.get();
// 檢查線程池狀態(tài),線程池處于RUNNABLE或者SHUTDOWN則進入
if (runStateLessThan(c, STOP)) {
if (!completedAbruptly) {
// 線程池最小數(shù)量嵌洼,取決于是否能釋放核心線程
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
// 如果任務隊列還有線程案疲,最起碼都要有一個線程來處理任務
if (min == 0 && ! workQueue.isEmpty())
min = 1;
if (workerCountOf(c) >= min)
return;
}
// 因為線程中斷,可能導致沒有線程來執(zhí)行阻塞隊列里的任務
// 因此嘗試創(chuàng)建線程去執(zhí)行任務
addWorker(null, false);
}
}
釋放工作線程也并沒有區(qū)分核心與非核心麻养,也是隨機進行的褐啡。所謂隨機,就是在前面所說的區(qū)間范圍內(nèi)鳖昌,根據(jù)釋放策略备畦,哪個線程先達到獲取不到任務的狀態(tài),就釋放哪個線程许昨。
文中多次出現(xiàn)tryTerminate()懂盐,但不深入去看了。里邊最主要的操作是糕档,發(fā)現(xiàn)可以中止線程池時莉恼,中止,并調(diào)用terminated()進行通知速那。如果線程池處于RUNNABLE狀態(tài)俐银,什么也不做,否則嘗試中斷一個線程琅坡。 中斷線程則是通過interruptIdleWorker()操作悉患,就不展開了。
到這里就能能明白線程池的原理的榆俺,如下圖
線程池里有容納一定的Worker售躁,Worker中的線程就是線程池中用來執(zhí)行任務的線程坞淮。當有任務加入線程時,根據(jù)線程池狀態(tài)的不同陪捷,有不同的步驟回窘。當核心線程未滿時,創(chuàng)建新線程來執(zhí)行市袖;否則將任務加入到阻塞隊列啡直;否則創(chuàng)建非核心線程來執(zhí)行。而線程獲取任務的方式有兩種苍碟,根據(jù)線程池容量區(qū)間酒觅,以及是否可以釋放核心線程來使用take()或者poll()來獲取任務,其中poll()在一定時間內(nèi)獲取不到任務微峰,則當前線程會被釋放舷丹。
當然,在addWorker()方法來有任務添加失敗的策略蜓肆,也就是RejectedExecutionHandler颜凯。ThreadPoolExecutor實現(xiàn)了四種策略來進行處理,簡單了解即可:
- CallerRunsPolicy: 如果線程池沒有SHUTODOWN的話仗扬,直接執(zhí)行任務
- AbortPolicy: 拋出異常症概,說明當前情況的線程池不希望得到接收不了任務的狀態(tài)
- DiscardOldestPolicy: 丟棄阻塞隊列最舊的任務
- DiscardPolicy: 什么也不做
需要注意的是,默認情況下策略為AbortPolicy早芭。
總結
做個總結:
- 線程池傾向于使用核心線程來處理任務彼城,從任務的添加策略可以看出,先考慮創(chuàng)建核心線程處理逼友,再考慮放到阻塞隊列精肃,再考慮創(chuàng)建非核心線程處理。以上都不行帜乞,則使用任務拒絕策略
- 通過向阻塞隊列取任務的不同操作司抱,能確保線程的存活,take()保證核心線程不死黎烈,poll()保證非核心線程存活等待一定時間
- 線程池不區(qū)分核心線程和非核心線程习柠,線程池是期望達到corePoolSize的并發(fā)狀態(tài),并允許在不得已情況下超載照棋,達到corePoolSize ~ maximumPoolSize 的并發(fā)狀態(tài)
- 線程池狀態(tài)和線程數(shù)量用ctl表示资溃,高三位為狀態(tài),低29位為當前線程池數(shù)量
- 線程池對狀態(tài)的檢測非沉姨浚苛刻溶锭,幾乎在所有稍微耗時或影響下一步操作正確性的代碼前都校驗ctl
線程池中有很多值得學習的東西,線程容量調(diào)整的設計符隙、ctl的設計趴捅、任務調(diào)度的設計等垫毙。也有需要更深的儲備才能看懂的實現(xiàn),這里點出拱绑,以備近一步學習综芥,如同步器的使用,并發(fā)場景的考慮與應用等猎拨。
下面回答開篇提出的問題膀藐。
問答
線程池如何實現(xiàn)
總結就是這個問題的答案
非核心線程延遲死亡,如何實現(xiàn)
通過阻塞隊列poll()红省,讓線程阻塞等待一段時間额各,如果沒有取到任務,則線程死亡
核心線程為什么不死
通過阻塞隊列take()吧恃,讓線程一直等待臊泰,直到獲取到任務
如何釋放核心線程
將allowCoreThreadTimeOut設置為true⊙潦啵可用下面代碼實驗
// 偽代碼
{
// 允許釋放核心線程,等待時間為100毫秒
es.allowCoreThreadTimeOut(true);
for(......){
// 向線程池里添加任務针饥,任務內(nèi)容為打印當前線程池線程數(shù)
Thread.currentThread().sleep(200);
}
}
線程數(shù)會一直為1厂抽。 如果allowCoreThreadTimeOut為false,線程數(shù)會逐漸達到飽和丁眼,然后大家一起阻塞等待筷凤。
非核心線程能成為核心線程嗎
線程池不區(qū)分核心線程于非核心線程,只是根據(jù)當前線程池容量狀態(tài)做不同的處理來進行調(diào)整苞七,因此看起來像是有核心線程于非核心線程藐守,實際上是滿足線程池期望達到的并發(fā)狀態(tài)。
Runnable在線程池里如何執(zhí)行
線程執(zhí)行Worker蹂风,Worker不斷從阻塞隊列里獲取任務來執(zhí)行卢厂。如果任務加入線程池失敗,則在拒絕策略里惠啄,還有處理機會慎恒。
線程數(shù)如何做選擇
這就要看任務類型是計算密集型任務還是IO密集型任務了,區(qū)別在于CPU占用率撵渡。計算密集型任務涉及內(nèi)存數(shù)據(jù)的存取融柬,CPU處于忙綠狀態(tài),因此并發(fā)數(shù)相應要低一些趋距。而IO密集型任務粒氧,因為外部設備速度不匹配問題,CPU更多是處于等待狀態(tài)节腐,因此可以把時間片分給其他線程外盯,因此并發(fā)數(shù)可以高一些摘盆。
常見的不同類型的線程池的功效如何做到
常見的線程池有:
- CachedThreadPool:適合異步任務多,但周期短的場景
- FixedThreadPool: 適合有一定異步任務门怪,周期較長的場景骡澈,能達到有效的并發(fā)狀態(tài)
- SingleThreadExecutor: 適合任務串行的場景
- ScheduledThreadPool: 適合周期性執(zhí)行任務的場景
對于如何選擇線程池就要看具體的場景,其中的差異通過構造參數(shù)可以到達效果掷空,通過之前的分析肋殴,就能知道參數(shù)的具體作用以及為什么能達到效果。取FixedThreadPool來看坦弟,拋磚引玉护锤。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
nThreads個數(shù)量核心線程持續(xù)并發(fā)任務,沒有非核心線程酿傍,如果沒有任務烙懦,則通過take()阻塞等待,不允許核心線程死亡赤炒。并且阻塞隊列為LinkedBlockingQueue氯析,容量為Integer.MAX_VALUE,可以視為無界隊列莺褒,更難走到拒絕添加線程邏輯掩缓。
參考
線程池原理
徹底理解Java線程池原理篇
Java線程池---ThreadPoolExecutor中的ctl變量
JUC鎖框架_AbstractQueuedSynchronizer詳細分析