前言
在上一篇文章【Java線程池的使用】中椎工,我們分析了線程池的用法。但那僅僅是用法,關于線程池內部是如何實現(xiàn)的维蒙,我們卻沒有深入分析掰吕。本著知其然,知其所以然
的想法颅痊,樓主將嘗試深入到線程池源碼去一窺究竟殖熟。
在jdk里面,線程池最重要的實現(xiàn)是ThreadPoolExecutor
斑响。因此菱属,我們分析的重點就是這個類,主要包括線程池狀態(tài)舰罚、線程池變量纽门、構造方法、提交任務等內容营罢。
線程池狀態(tài)
線程池可以包含多個線程赏陵,線程池本身又維護著自己的狀態(tài)。在ThreadPoolExecutor
中饲漾,線程池狀態(tài)和worker數(shù)量都是由ctl變量控制蝙搔。
那么,ctl變量又是如何控制線程池狀態(tài)的呢考传?我們先來看看ctl相關的源碼吃型。
// 1. `ctl`,可以看做一個int類型的數(shù)字僚楞,高3位表示線程池狀態(tài)勤晚,低29位表示worker數(shù)量
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// 2. `COUNT_BITS`,`Integer.SIZE`為32镜硕,所以`COUNT_BITS`為29
private static final int COUNT_BITS = Integer.SIZE - 3;
// 3. `CAPACITY`运翼,線程池允許的最大線程數(shù)返干。1左移29位兴枯,然后減1,即為 2^29 - 1
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// runState is stored in the high-order bits
// 4. 線程池有5種狀態(tài)矩欠,按大小排序如下:RUNNING < SHUTDOWN < STOP < TIDYING < TERMINATED
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
// Packing and unpacking ctl
// 5. `runStateOf()`财剖,獲取線程池狀態(tài),通過按位與操作癌淮,低29位將全部變成0
private static int runStateOf(int c) { return c & ~CAPACITY; }
// 6. `workerCountOf()`躺坟,獲取線程池worker數(shù)量,通過按位與操作乳蓄,高3位將全部變成0
private static int workerCountOf(int c) { return c & CAPACITY; }
// 7. `ctlOf()`咪橙,根據線程池狀態(tài)和線程池worker數(shù)量,生成ctl值
private static int ctlOf(int rs, int wc) { return rs | wc; }
/*
* Bit field accessors that don't require unpacking ctl.
* These depend on the bit layout and on workerCount being never negative.
*/
// 8. `runStateLessThan()`,線程池狀態(tài)小于xx
private static boolean runStateLessThan(int c, int s) {
return c < s;
}
// 9. `runStateAtLeast()`美侦,線程池狀態(tài)大于等于xx
private static boolean runStateAtLeast(int c, int s) {
return c >= s;
}
在上面的源碼中产舞,每個常量和方法都很關鍵。這兒菠剩,我們對每個關鍵點都進行一下解釋易猫。
-
ctl
,可以看做一個int類型的數(shù)字具壮,高3位表示線程池狀態(tài)准颓,低29位表示線程的數(shù)量 -
COUNT_BITS
,Integer.SIZE
為32棺妓,所以COUNT_BITS
為29 -
CAPACITY
攘已,線程池允許的最大線程數(shù)。1左移29位涧郊,然后減1贯被,即為 2^29 - 1 - 線程池有5種狀態(tài),按大小排序如下:RUNNING < SHUTDOWN < STOP < TIDYING < TERMINATED
-
runStateOf()
妆艘,獲取線程池狀態(tài)彤灶,通過按位與操作,低29位將全部變成0 -
workerCountOf()
批旺,獲取線程池worker數(shù)量幌陕,通過按位與操作,高3位將全部變成0 -
ctlOf()
汽煮,根據線程池狀態(tài)和線程池worker數(shù)量搏熄,生成ctl值 -
runStateLessThan()
,線程池狀態(tài)小于xx -
runStateAtLeast()
暇赤,線程池狀態(tài)大于等于xx
為了加深對這段代碼的理解心例,我們將常量對應的二進制數(shù)以表格的形式列出來,如下所示鞋囊。
常量 | 二進制數(shù) |
---|---|
RUNNING | 11100000000000000000000000000000 |
SHUTDOWN | 00000000000000000000000000000000 |
STOP | 00100000000000000000000000000000 |
TIDYING | 01000000000000000000000000000000 |
TERMINATED | 01100000000000000000000000000000 |
CAPACITY | 00011111111111111111111111111111 |
~CAPACITY | 11100000000000000000000000000000 |
構造方法
ThreadPoolExecutor
有多個重載構造方法止后,但是他們最終調用了同一個構造方法。該方法非常簡單溜腐,主要做了兩件事情:
- 對輸入參數(shù)進行校驗并設置到成員變量
- 根據輸入參數(shù)
unit
和keepAliveTime
译株,將存活時間轉換為納秒存到變量keepAliveTime
中
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
// 基本類型參數(shù)校驗
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
// 空指針校驗
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
// 根據傳入參數(shù)`unit`和`keepAliveTime`,將存活時間轉換為納秒存到變量`keepAliveTime `中
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
使用線程池執(zhí)行任務 - execute()
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
int c = ctl.get();
// worker數(shù)量比核心線程數(shù)小挺益,直接創(chuàng)建worker執(zhí)行任務
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
// worker數(shù)量超過核心線程數(shù)歉糜,任務直接進入隊列
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
// 線程池狀態(tài)不是RUNNING狀態(tài),說明執(zhí)行過shutdown命令望众,需要對新加入的任務執(zhí)行reject()操作匪补。
// 這兒為什么需要recheck伞辛,是因為任務入隊列前后,線程池的狀態(tài)可能會發(fā)生變化夯缺。
if (! isRunning(recheck) && remove(command))
reject(command);
// 這兒為什么需要判斷0值始锚,主要是在線程池構造方法中,核心線程數(shù)允許為0
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 如果線程池不是運行狀態(tài)喳逛,或者任務進入隊列失敗瞧捌,則嘗試創(chuàng)建worker執(zhí)行任務。
// 這兒有3點需要注意:
// 1. 線程池不是運行狀態(tài)時润文,addWorker內部會判斷線程池狀態(tài)
// 2. addWorker第2個參數(shù)表示是否創(chuàng)建核心線程
// 3. addWorker返回false姐呐,則說明任務執(zhí)行失敗,需要執(zhí)行reject操作
else if (!addWorker(command, false))
reject(command);
}
我們已經分析完了execute()
方法典蝌,其內部調用了一些關鍵方法曙砂,如下所示:
- addWorker() // 創(chuàng)建worker
- reject() // 拒絕任務
由于reject()
較為簡單,我們先分析它骏掀,然后分析addWorker()
鸠澈。
拒絕任務 - reject()
private volatile RejectedExecutionHandler handler;
final void reject(Runnable command) {
handler.rejectedExecution(command, this);
}
只是調用了拒絕策略的回調方法。
添加worker - addWorker()
這個方法可以簡單地分成兩個部分截驮,一部分通過樂觀鎖+自旋
的方式增加worker的數(shù)量笑陈,另一部分通過悲觀鎖
的方式創(chuàng)建worker(一個worker可以簡單地被認為是線程池里的一個線程)。
先來看第一部分葵袭。
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
// 外層自旋
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// 這個條件寫得比較難懂涵妥,我對其進行了調整,和下面的條件等價
// (rs > SHUTDOWN) ||
// (rs == SHUTDOWN && firstTask != null) ||
// (rs == SHUTDOWN && workQueue.isEmpty())
// 1. 線程池狀態(tài)大于SHUTDOWN時坡锡,直接返回false
// 2. 線程池狀態(tài)等于SHUTDOWN蓬网,且firstTask不為null,直接返回false
// 3. 線程池狀態(tài)等于SHUTDOWN鹉勒,且隊列為空帆锋,直接返回false
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
// 內層自旋
for (;;) {
int wc = workerCountOf(c);
// worker數(shù)量超過容量,直接返回false
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
// 使用CAS的方式增加worker數(shù)量禽额。
// 若增加成功锯厢,則直接跳出外層循環(huán)進入到第二部分
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
// 線程池狀態(tài)發(fā)生變化,對外層循環(huán)進行自旋
if (runStateOf(c) != rs)
continue retry;
// 其他情況绵疲,直接內層循環(huán)進行自旋即可
// else CAS failed due to workerCount change; retry inner loop
}
}
// ...
}
接下來我們分析第二部分哲鸳。
private boolean addWorker(Runnable firstTask, boolean core) {
// ...
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
// worker的添加必須是串行的臣疑,因此需要加鎖
mainLock.lock();
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
// 這兒需要重新檢查線程池狀態(tài)
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
// worker已經調用過了start()方法盔憨,則不再創(chuàng)建worker
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
// worker創(chuàng)建并添加到workers成功
workers.add(w);
// 更新`largestPoolSize`變量
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
// 啟動worker線程
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
// worker線程啟動失敗,說明線程池狀態(tài)發(fā)生了變化(關閉操作被執(zhí)行)讯沈,需要進行shutdown相關操作
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
通過對第一部分和第二部分的分析郁岩,我們知道了線程池中的線程是封裝在Worker
中執(zhí)行的婿奔。下一步,我們就從Worker
進行分析问慎。
線程池執(zhí)行單元 - Worker
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
/**
* This class will never be serialized, but we provide a
* serialVersionUID to suppress a javac warning.
*/
private static final long serialVersionUID = 6138294804551838833L;
/** Thread this worker is running in. Null if factory fails. */
final Thread thread;
/** Initial task to run. Possibly null. */
Runnable firstTask;
/** Per-thread task counter */
volatile long completedTasks;
/**
* Creates with given first task and thread from ThreadFactory.
* @param firstTask the first task (null if none)
*/
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
// 這兒是Worker的關鍵所在萍摊,使用了線程工廠創(chuàng)建了一個線程。傳入的參數(shù)為當前worker
this.thread = getThreadFactory().newThread(this);
}
/** Delegates main run loop to outer runWorker */
public void run() {
runWorker(this);
}
// 省略AQS相關的代碼...
}
上述代碼為Worker
的基本實現(xiàn)(省略了AQS相關的代碼)如叼。我們看到Worker
實現(xiàn)了兩個接口冰木,Runnable
和AbstractQueuedSynchronizer
。
關于AQS相關的知識笼恰,我們在AbstractQueuedSynchronizer(AQS)源碼解析做了比較全面的說明踊沸。因此,這兒我們更多的是關心run()
方法的實現(xiàn)社证。
run()
方法其實調用了ThreadPoolExecutor.runWorker()
方法逼龟,且把當前Worker
對象傳遞了進去。
核心線程執(zhí)行邏輯 - runWorker()
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
// 調用unlock()是為了讓外部可以中斷
w.unlock(); // allow interrupts
// 這個變量用于判斷是否進入過自旋(while循環(huán))
boolean completedAbruptly = true;
try {
// 這兒是自旋
// 1. 如果firstTask不為null追葡,則執(zhí)行firstTask腺律;
// 2. 如果firstTask為null,則調用getTask()從隊列獲取任務宜肉。
// 3. 阻塞隊列的特性就是:當隊列為空時匀钧,當前線程會被阻塞等待
while (task != null || (task = getTask()) != null) {
// 這兒對worker進行加鎖,是為了達到下面的目的
// 1. 降低鎖范圍谬返,提升性能
// 2. 保證每個worker執(zhí)行的任務是串行的
w.lock();
// If pool is stopping, ensure thread is interrupted;
// if not, ensure thread is not interrupted. This
// requires a recheck in second case to deal with
// shutdownNow race while clearing interrupt
// 如果線程池正在停止榴捡,則對當前線程進行中斷操作
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
// 執(zhí)行任務,且在執(zhí)行前后通過`beforeExecute()`和`afterExecute()`來擴展其功能朱浴。
// 這兩個方法在當前類里面為空實現(xiàn)吊圾。
try {
beforeExecute(wt, task);
Throwable thrown = null;
try {
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 {
afterExecute(task, thrown);
}
} finally {
// 幫助gc
task = null;
// 已完成任務數(shù)加一
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
// 自旋操作被退出,說明線程池正在結束
processWorkerExit(w, completedAbruptly);
}
}
該方法是線程池執(zhí)行任務的關鍵所在翰蠢。雖然是核心功能项乒,但是本身的邏輯卻非常簡單和高效。該方法主要通過自旋梁沧、阻塞隊列的阻塞特性和worker加鎖的方式來完成任務的執(zhí)行檀何。除此之外,使用了finally機制
來釋放資源(處理線程池結束的后續(xù)操作)廷支。
worker結束處理邏輯 - processWorkerExit
private void processWorkerExit(Worker w, boolean completedAbruptly) {
// 這個變量用于表示是否進入過自旋频鉴。
// 1. 如果沒有進入過,該值為false
// 2. 進入過恋拍,該值為true
// 只有進入過自旋垛孔,worker的數(shù)量才需要減一
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
decrementWorkerCount();
// 通過全局鎖的方式移除worker
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
completedTaskCount += w.completedTasks;
workers.remove(w);
} finally {
mainLock.unlock();
}
// 嘗試終止線程池
tryTerminate();
int c = ctl.get();
// 如果線程池狀態(tài)為`SHUTDOWN`或`RUNNING`,
// 則通過調用`addWorker()`來創(chuàng)建線程施敢,輔助完成對阻塞隊列中任務的處理周荐。
if (runStateLessThan(c, STOP)) {
if (!completedAbruptly) {
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
if (min == 0 && ! workQueue.isEmpty())
min = 1;
if (workerCountOf(c) >= min)
return; // replacement not needed
}
addWorker(null, false);
}
}
該方法主要通過全局鎖的方式來移除當前worker狭莱,同時如果線程池狀態(tài)是SHUTDOWN
或RUNNING
時,則通過調用addWorker()
來創(chuàng)建線程概作,輔助完成對阻塞隊列中任務的處理腋妙。
線程池的退出
線程池退出的方式有兩種,一種為優(yōu)雅型讯榕,一種為強迫型骤素。
- 優(yōu)雅型會等待所有已提交到線程池的任務執(zhí)行完成之后才結束線程池。
- 強迫型會對當前正在執(zhí)行的任務進行中斷操作愚屁,并將阻塞隊列里面的任務以
List<Runnable>
的方式返回給調用方谆甜,由調用方做后續(xù)處理。
優(yōu)雅型退出 - shutdown()
public void shutdown() {
// 使用全局鎖來操作線程池終止
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 權限校驗
checkShutdownAccess();
// 通過CAS的方式變更線程池狀態(tài)為`SHUTDOWN`
advanceRunState(SHUTDOWN);
// 中斷空閑的worker集绰,這也是和`shutdownNow()`不同的地方
interruptIdleWorkers();
// 留給ScheduledThreadPoolExecutor處理
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate();
}
關鍵的地方為interruptIdleWorkers()
规辱,該方法用于中斷空閑的worker。我們來看看這個方法栽燕,然后對其進行補充說明罕袋。
private void interruptIdleWorkers() {
interruptIdleWorkers(false);
}
// 1. `onlyOne`用于控制是否只循環(huán)一次,在`shutdown()`里面碍岔,該值為false浴讯,因此是遍歷整個worker列表
private void interruptIdleWorkers(boolean onlyOne) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers) {
Thread t = w.thread;
// 2. 對每個worker,會嘗試去獲取worker鎖蔼啦。
// 如果獲取到了榆纽,那么說明該worker是空閑的,可以進行中斷操作
if (!t.isInterrupted() && w.tryLock()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
if (onlyOne)
break;
}
} finally {
mainLock.unlock();
}
}
interruptIdleWorkers()
做了兩件事情:
-
onlyOne
用于控制是否只循環(huán)一次捏肢,在shutdown()
里面奈籽,該值為false,因此是遍歷整個worker列表 - 對每個worker鸵赫,會嘗試去獲取worker鎖衣屏。如果獲取到了,那么說明該worker是空閑的辩棒,可以進行中斷操作
強迫型退出 - shutdownNow()
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
// 使用全局鎖來操作線程池終止
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 權限校驗
checkShutdownAccess();
// 通過CAS的方式變更線程池狀態(tài)為`STOP`
advanceRunState(STOP);
// 中斷所有的worker狼忱,這也是和`shutdown()`不同的地方
interruptWorkers();
// 將阻塞隊列里面的任務排出到tasks
tasks = drainQueue();
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
該方法和shutdown()
不同的地方在于對worker的處理方式。shutdownNow()
處理得非常得強勢一睁,調用了interruptWorkers()
方法钻弄。我們來看看此方法,然后作出說明者吁。
private void interruptWorkers() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 1. 對所有worker調用`interruptIfStarted()`方法
for (Worker w : workers)
w.interruptIfStarted();
} finally {
mainLock.unlock();
}
}
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
void interruptIfStarted() {
Thread t;
// 2.線程池狀態(tài)為`非RUNNING`時窘俺,直接對worker包裝的線程進行中斷操作
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
}
該方法做了兩件事情:
- 對所有worker調用
interruptIfStarted()
方法 - 線程池狀態(tài)為
非RUNNING
時,直接對worker包裝的線程進行中斷操作
線程池狀態(tài)變更 - advanceRunState()
在shutdown()
和shutdownNow()
中砚偶,我們都看到了對advanceRunState()
方法的調用批销。該方法用于線程安全地變更線程池狀態(tài),那么它是如何做到線程安全的呢染坯?
private void advanceRunState(int targetState) {
// 1. 自旋操作
for (;;) {
// 2. 通過CAS的方式保證線程池狀態(tài)變更的線程安全性
int c = ctl.get();
if (runStateAtLeast(c, targetState) ||
ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
break;
}
}
PS:我們可能很好奇均芽,
advanceRunState()
方法為什么要用CAS呢?在shutdown()
和shutdownNow()
里面不是有加鎖嗎单鹿?
請注意掀宋,外層方法的確有加鎖,但是和worker線程所使用的鎖并不是同一個仲锄,因此需要通過自旋+CAS的方式來額外保證線程安全劲妙!
阻塞隊列任務排出 - drainQueue()
private List<Runnable> drainQueue() {
BlockingQueue<Runnable> q = workQueue;
ArrayList<Runnable> taskList = new ArrayList<Runnable>();
q.drainTo(taskList);
if (!q.isEmpty()) {
for (Runnable r : q.toArray(new Runnable[0])) {
if (q.remove(r))
taskList.add(r);
}
}
return taskList;
}
該方法通過兩個階段來保證導出任務的完整性。
- 調用阻塞隊列的
drainTo()
方法 - 遍歷阻塞隊列剩余的元素儒喊,通過
remove()&add()
的方式來導出元素镣奋。
在drainTo()
的過程中可能有新元素進來,這也是需要兩個階段來完成導出的原因怀愧。
帶返回結果的任務提交 - submit()
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
// ftask既是Runnable侨颈,也是Future
RunnableFuture<T> ftask = newTaskFor(task);
// 執(zhí)行任務
execute(ftask);
return ftask;
}
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable {
return new FutureTask<T>(callable);
}
submit()
內部調用的仍然是execute()
方法,只是調完之后返回了Future
對象芯义,我們可以通過這個對象的get()
方法來拿到最終執(zhí)行的結果哈垢。
到這里,我們必須打住扛拨,F(xiàn)uture相關的知識點不在本節(jié)的討論范圍內耘分。
因為Future
和FutureTask
非常重要,樓主計劃單獨寫一篇博客來做闡述绑警。Future
相關的東西比較多求泰,而且復雜。為了不影響本問的初衷计盒,至此暫且略過拜秧。
總結
本文通過對Java線程池代碼的分析,使得我們知道了其大概的實現(xiàn)方式章郁。
在Java線程池出現(xiàn)之前枉氮,如果我們想異步執(zhí)行一個任務,往往是先創(chuàng)建一個線程暖庄,然后執(zhí)行任務聊替,執(zhí)行完之后就扔掉了,最后等待GC對創(chuàng)建線程的回收培廓。而這樣處理的后果惹悄,往往是非常昂貴的內存和CPU開銷。
Java線程池比較徹底地解決了這個問題肩钠!其內部實現(xiàn)合理地運用了自旋泣港、CAS暂殖、樂觀鎖、悲觀鎖当纱、recheck重測呛每、狀態(tài)機、原子變量等等各種并發(fā)技術坡氯,充分有效地發(fā)揮了CPU的多核計算能力晨横。由此可見并發(fā)大師Doug Lea設計和編碼的功力之深,簡直是前無古人箫柳,后無來者手形?
我們將Java線程池的分析放在這一篇博客中,其實有點管中窺豹之嫌悯恍。很多的細節(jié)库糠,我們往往是一筆代過。但是涮毫,大致的結構和功能卻是分析到了的曼玩。
最后,樓主希望本文能對讀者有所幫助窒百!