在開始解析線程池之前,先簡(jiǎn)單的總結(jié)一下創(chuàng)建線程的幾種方式:
- 繼承Thread類
- 實(shí)現(xiàn)Runnable接口
- 使用Future和Callable
- 借助線程池
上面是創(chuàng)建一個(gè)線程的四種方式适秩,在實(shí)際的開發(fā)中是推薦使用線程池來實(shí)現(xiàn)多線程的并發(fā)操作绊序。ThreadPoolExecutor是線程池的核心實(shí)現(xiàn)類,用來執(zhí)行被提交的任務(wù)秽荞。
在開始分析之前骤公,先看一下線程池的主要處理流程,如下圖:
上圖是線程池的處理流程扬跋,先從宏觀上阶捆,看一下處理流程,下面結(jié)合代碼看看具體是如何實(shí)現(xiàn)的钦听,那么開始分析:
數(shù)據(jù)結(jié)構(gòu)
繼承關(guān)系
public class ThreadPoolExecutor extends AbstractExecutorService
類的定義非常的簡(jiǎn)單洒试,只是繼承了一個(gè)抽象類 AbstractExecutorService 。并沒有很好的反映出它的一個(gè)繼承關(guān)系朴上, 不要著急垒棋,看一下下面的這張圖:
通過上圖可以發(fā)現(xiàn),Executor才是站在金子塔頂端的那個(gè)痪宰, 由它來掌控全局叼架, 當(dāng)然Executor這個(gè)接口也是非常的高冷畔裕, 只定義了一個(gè) void execute(Runnable command)
方法,將任務(wù)的提交與任務(wù)的執(zhí)行進(jìn)行了分離乖订。ExecutorService接口繼承了Executor 接口扮饶,添加了一些帶返回的submit()方法和關(guān)閉方法shutdown()等。 AbstractExecutorService抽象類實(shí)現(xiàn)了ExecutorService()提供了一些方法的默認(rèn)實(shí)現(xiàn)乍构,ThreadPoolExector繼承了抽象類甜无,并實(shí)現(xiàn)了Executor接口的execute()方法。
基本屬性
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
ctl是ThreadPoolThread類中很重要的一個(gè)字段蜡吧,它是一個(gè)特殊的int類型變量。 并且是一個(gè)原子操作占键,能保證線程的安全性昔善。 ctl這個(gè)變量包含了兩部分的信息 , 簡(jiǎn)寫表示 c:
- workerCount:線程池內(nèi)有效線程的數(shù)量 : 簡(jiǎn)寫表示 wc
- runState:線程池的運(yùn)行狀態(tài) ,running ,shutdown 等 ,簡(jiǎn)寫表示為 rs
它是怎么做到一個(gè)變量包含兩個(gè)信息的,詳細(xì)分析一下:
一個(gè)int類型的變量用二進(jìn)制表示是32 位的畔乙,其中高3為表示的是線程池的狀態(tài)君仆,低29位表示的線程中有效線程的數(shù)量。 這設(shè)計(jì)厲害了牲距,還可以這么玩返咱。
runState是一個(gè)生命周期的控制狀態(tài),值如下:
- RUNNING : 接受新任務(wù)并處理隊(duì)列任務(wù) 牍鞠, 數(shù)值表示 -1 << 29
- SHUTDOWN :不接受新任務(wù)并處理隊(duì)列任務(wù) ,數(shù)值表示 0 << 29
- STOP : 不接受新任務(wù)咖摹,不處理隊(duì)列任務(wù),并且會(huì)中斷正在處理的線程任務(wù),數(shù)值表示 1 << 29
- TIDYING : 所有任務(wù)終止难述,workCount為0萤晴,清理狀態(tài) 數(shù)值表示 2 << 29
- TERMINATED :terminated()方法執(zhí)行后執(zhí)行該狀態(tài) ,數(shù)值表示 3 << 29
上面的5個(gè)常量,按照從小到大的順序依次性進(jìn)行排列胁后。線程池的運(yùn)行狀態(tài)要小于線程池的其它狀態(tài)店读。
上面說了ctl中包含了兩部分信息,下面的代碼是它們的具體實(shí)現(xiàn):
先看將兩部分合并的方法:
//將runState和workerCount 合并到一個(gè)ctl中
//RUNNING的值為 -1 << 29 , wc 為 0
//rs的二進(jìn)制表示為: 11100000000000000000000000000000
//wc二進(jìn)制表示為: 00000000000000000000000000000000
// -------------------------------- | 運(yùn)算
//所以兩者 | 運(yùn)算后:值為 11100000000000000000000000000000
//rs填充ctl的高三位攀芯, wc填充ctl的低29 位 屯断,初始化wc的值為零
private static int ctlOf(int rs, int wc) { return rs | wc; }
再看將ctl分解為runState和workCount方法
//該方法用于取出runState的值
//00011111111111111111111111111111 CAPACITY的二進(jìn)制表示
//11100000000000000000000000000000 ~ 按位取反操作,如果是1111 取反就是 0000
//11100000000000000000000000000000 c的二進(jìn)制表示
//-------------------------------- 運(yùn)算
//11100000000000000000000000000000 返回結(jié)果
private static int runStateOf(int c) { return c & ~CAPACITY; }
//該方法用于取出workCount的值
//11100000000000000000000000000000
//00011111111111111111111111111111
//--------------------------------
//00000000000000000000000000000000 返回結(jié)果
private static int workerCountOf(int c) { return c & CAPACITY; }
上面演示了在初始化線程池的時(shí)候侣诺,c , rs , wc 是如何運(yùn)算的殖演。如果沒有明白,請(qǐng)去看一下位移運(yùn)算年鸳。
構(gòu)造方法
ThreadPoolExecutor提供了四個(gè)構(gòu)造方法剃氧,會(huì)涉及到幾個(gè)非常重要的參數(shù),構(gòu)造方法看下面的這一個(gè)就可以了阻星,因?yàn)槠渌娜齻€(gè)構(gòu)造方法朋鞍,都是使用的this調(diào)用的下面這個(gè)方法已添。ThreadPoolExecutor的構(gòu)造方法除了進(jìn)行參數(shù)的合法性和賦值賦值操作外,并沒有其他多余的動(dòng)作滥酥。
//corePoolSize : 核心線程池的實(shí)現(xiàn)大小
//maximumPoolSize : 最大線程池的實(shí)現(xiàn)大小
//keepAliveTime :線程活動(dòng)保持時(shí)間更舞,線程空閑超過這個(gè)時(shí)間就會(huì)終止
//unit : 線程活動(dòng)保持時(shí)間的單位
//workQueue :用來暫時(shí)保存任務(wù)的工作隊(duì)列
//threadFactory:用于設(shè)置創(chuàng)建線程的工廠,可以通過線程工廠給每個(gè)線程設(shè)置有意義的名字
//handler : 當(dāng)ThreadPoolExecutor已經(jīng)關(guān)閉或已經(jīng)飽和時(shí)坎吻,execute()方法將調(diào)用Handler
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
//判斷參數(shù)合法性缆蝉,不合法拋出IllegalArgumentException異常
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
//隊(duì)列,線程工廠瘦真,handler為空拋出空指針異常
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
//賦值操作
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
上面只是簡(jiǎn)單的介紹了一下參數(shù)的含義刊头,下面具體說明一下:
corePoolSize:當(dāng)提交一個(gè)任務(wù)時(shí),線程池會(huì)創(chuàng)建一個(gè)線程來執(zhí)行任務(wù)诸尽,即使有空閑線程也會(huì)創(chuàng)建一個(gè)新的原杂,等到需要執(zhí)行的任務(wù)數(shù)大于線程池基本大小時(shí)就不再創(chuàng)建。如果調(diào)用了prestartAllCoreThreads()方法您机,線程池會(huì)提前創(chuàng)建并啟動(dòng)所有基本的線程穿肄。
-
workQueue:用于保存等待執(zhí)行任務(wù)的阻塞隊(duì)列
- ArrayBlockingQueue:基于數(shù)組結(jié)構(gòu)的有界阻塞隊(duì)列
- LinkedBlockingQueue:基于鏈表結(jié)構(gòu)的阻塞隊(duì)列,按照FIFO排序元素
- SynchronousQueue:一個(gè)不存儲(chǔ)元素的阻塞隊(duì)列
- PriorityBlockingQueue:一個(gè)具有優(yōu)先級(jí)的無限阻塞隊(duì)列
maximumPoolSize:線程池允許創(chuàng)建的最大線程數(shù)际看,如果隊(duì)列滿了咸产,并且已經(jīng)創(chuàng)建的線程數(shù)小于最大線程數(shù),則線程池會(huì)創(chuàng)建新的線程去執(zhí)行任務(wù)仲闽。
ThreadFactory:創(chuàng)建線程的工廠
-
RejectedExecutionHandler:當(dāng)隊(duì)列和線程池都滿了脑溢,說明線程池已經(jīng)處于飽和狀態(tài),那么必須采取一種策略來處理新提交的任務(wù)
- AbortPolicy:直接拋出異常
- CallerRunsPolicy:只用調(diào)用者所在的線程來運(yùn)行任務(wù)
- DiscardOldestPloicy:丟棄隊(duì)列里最近的一個(gè)任務(wù)赖欣,并執(zhí)行當(dāng)前任務(wù)
- DiscardPolicy:不處理焚志,丟棄掉
向線程池提交任務(wù)
提交任務(wù)有兩個(gè)方法,一個(gè)是execute()一個(gè)是submit()方法畏鼓,前一個(gè)不會(huì)返回值酱酬,后一個(gè)會(huì)返回值。submit()方法不再本次解析的范圍內(nèi)云矫,感興趣的可以自行研究簡(jiǎn)單提一下膳沽,submit()方法在AbstractExecutorService抽象類中實(shí)現(xiàn) ,借助 FutureTask進(jìn)行封裝让禀,在execute() 中執(zhí)行挑社。
execute()方法
對(duì)于線程池,作為使用方的我們只能向線程池提交任務(wù)巡揍,而對(duì)于任務(wù)是否執(zhí)行和什么時(shí)候執(zhí)行痛阻,并不能控制。
public void execute(Runnable command) {
//驗(yàn)證參數(shù)合法性
if (command == null)
throw new NullPointerException();
//獲取ctl的值
int c = ctl.get();
//如果當(dāng)前線程池中線程的數(shù)量小于核心線程的數(shù)量
if (workerCountOf(c) < corePoolSize) {
//執(zhí)行addWorker方法腮敌,
//addWorker 將任務(wù)添加到隊(duì)列阱当,并啟動(dòng)俏扩,成功返回true,失敗返回false
if (addWorker(command, true))
//addWorker返回true, 添加成功弊添,結(jié)束execute()方法運(yùn)行
return;
c = ctl.get();
}
//線程池的運(yùn)行狀態(tài)已經(jīng)不是RUNNING
//線程池的狀態(tài)是RUNNING录淡,wc > corePoolSize,隊(duì)列未滿
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
// 非RUNNING狀態(tài) 則從workQueue中移除任務(wù)并拒絕
if (! isRunning(recheck) && remove(command))
reject(command);
// 線程池處于RUNNING狀態(tài) || 線程池處于非RUNNING狀態(tài)但是任務(wù)移除失敗
else if (workerCountOf(recheck) == 0)
// 這行代碼是為了SHUTDOWN狀態(tài)下沒有活動(dòng)線程了油坝,但是隊(duì)列里還有任務(wù)沒執(zhí)行這種特殊情況嫉戚。
// 添加一個(gè)null任務(wù)是因?yàn)镾HUTDOWN狀態(tài)下,線程池不再接受新任務(wù)
//但要運(yùn)行任務(wù)隊(duì)列中的任務(wù)
addWorker(null, false);
}
//線程池非RUNNING狀態(tài)
//隊(duì)列滿了澈圈,啟動(dòng)新的線程也失敗了彬檀,采用拒絕策略
else if (!addWorker(command, false))
reject(command);
}
上面的注釋已經(jīng)詳細(xì)的標(biāo)注了if中判斷的條件,這里在簡(jiǎn)單的總結(jié)一下瞬女,execute()方法的添加策略窍帝,分為幾種情形:
- wc < corePoolSize , 創(chuàng)建一個(gè)新的線程,并提交任務(wù)
- wc = corePoolSize , 任務(wù)隊(duì)列未滿拆魏,則添加到阻塞隊(duì)列
- corePoolSize < wc < maximumPoolSize ,阻塞隊(duì)列已滿盯桦,嘗試創(chuàng)建一個(gè)新的進(jìn)程來執(zhí)行任務(wù)
- wc >= maximunPoolSize ,阻塞隊(duì)列已滿慈俯,則采用拒絕隊(duì)列渤刃。
如果阻塞隊(duì)列是無界隊(duì)列,則 maximunPoolSize 這個(gè)參數(shù)就沒有什么效果贴膘。
上面的四種情形也和文章開始的流程圖相契合卖子,可以在回頭看一下。
addworker() 也在線程池的調(diào)度邏輯中扮演了很重要的角色刑峡,下面來看一下洋闽,它具體都進(jìn)行了哪些操作。
添加任務(wù)的方法邏輯可能有一點(diǎn)繞突梦, 多看幾遍就可以了诫舅。
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
//獲取ctl的值
int c = ctl.get();
//計(jì)算rs的值,用于判斷線程池當(dāng)前的狀態(tài)
int rs = runStateOf(c);
//下面的if判斷兩種情況
//1.rs >= SHUTDOWN ==> 線程池處于非運(yùn)行狀態(tài)
//2.線程池狀態(tài)為SHUTDOWN宫患,此時(shí)不再接收新的任務(wù)刊懈,但還要處理隊(duì)列中的任務(wù)
// firstTask !=null 不再接收新任務(wù), 隊(duì)列為空,任務(wù)已經(jīng)處理完成娃闲。都直接返回虚汛。
if (rs >= SHUTDOWN && ! (rs == SHUTDOWN &&firstTask == null && ! workQueue.isEmpty()))
return false;
//執(zhí)行到此處,說明線程處在RUNNING狀態(tài)
//或者 SHUTDOWN狀態(tài)皇帮,隊(duì)列中還有任務(wù)要執(zhí)行
for (;;) {
//計(jì)算線程池中有效線程的數(shù)量
int wc = workerCountOf(c);
//判斷wc的數(shù)量是否符合規(guī)則
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
//cas ,+1 操作 卷哩,成功返回true,失敗返回false
if (compareAndIncrementWorkerCount(c))
break retry; //退出循環(huán)
c = ctl.get(); // Re-read ctl
//+1 操作失敗, 從新檢測(cè)線程池的狀態(tài)属拾,繼續(xù)循環(huán)
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
//workCount +1 操作成功将谊,執(zhí)行下面的步驟
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
//Worker: 實(shí)現(xiàn)了Runnable的私有內(nèi)部類冷溶,將任務(wù)進(jìn)行封裝
w = new Worker(firstTask);
final Thread t = w.thread; //w.thread 由Work構(gòu)造方法初始化
if (t != null) {
//獲取全局鎖,并發(fā)訪問workers ,加鎖處理
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
int rs = runStateOf(ctl.get());
//RUNNING狀態(tài)瓢娜,SHUTDOWN狀態(tài)清理隊(duì)列中的任務(wù)
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
//將work添加到HashSet集合中挂洛。
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
//啟動(dòng)線程執(zhí)行任務(wù)
t.start(); //執(zhí)行的是ThreadPoolExecuteor的runWorker方法
workerStarted = true;
}
}
} finally {
//線程啟動(dòng)失敗
if (! workerStarted)
//刪除添加的任務(wù)
addWorkerFailed(w);
}
return workerStarted;
}
上面的注釋已經(jīng)很清楚,可能還有人覺得有些混亂眠砾,那么在串一下這個(gè)處理流程:
線程池的狀態(tài)在不斷的發(fā)生變化虏劲,添加新的任務(wù)時(shí),先判斷一下當(dāng)前的線程狀態(tài)褒颈。
在調(diào)用addWorker()之前柒巫,判斷了此時(shí)的wc數(shù)量是小于< corePoolSize的。那么也就說只要線程池狀態(tài)是RUNNING, 就可以直接創(chuàng)建新的線程來執(zhí)行提交的任務(wù)谷丸。但是還是要排除堡掏,線程池是非RUNNING的這種情況。if (rs >= SHUTDOWN && ! (rs == SHUTDOWN &&firstTask == null && !workQueue.isEmpty()))
便有了這個(gè)判斷條件刨疼。 所以如果addWorker()方法沒有創(chuàng)建新線程也沒有提交新任務(wù)泉唁,直接返回false的話,只能是下列情形中的一種:
- 線程池是STOP|TIDYING|TERMINATED 中的一種狀態(tài)
- 線程池是SHUNDOWN狀態(tài)揩慕,但 firstTask != null
- 線程池是SHUNDOWN狀態(tài)亭畜,但 workQueue 是空
- 線程的有效線程數(shù)量大于CAPACITY|| 大于(corePoolSize或者maximumPoolSize)
上面的 大于(corePoolSize或者maximumPoolSize)取決于隊(duì)列是否已滿
t.start()是調(diào)用的runWorker方法,因?yàn)閃orker的run()方法迎卤,調(diào)用的是ThreadPoolExecutor類中的RunWorker()方法拴鸵,看一下RunWorker的具體實(shí)現(xiàn)。
runWorker()
final void runWorker(Worker w) {
//獲取當(dāng)前運(yùn)行的線程
Thread wt = Thread.currentThread();
//獲取Worker中包裝的Runnable
Runnable task = w.firstTask;
w.firstTask = null;
//允許中斷蜗搔,因?yàn)樵趙ork的構(gòu)造方法中抑制了中斷
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
//task 就是 w.firstTask ,也就是包裝后的任務(wù)
//task 不是null 則進(jìn)入while循環(huán)中
//如果 task == null , 則從隊(duì)列中取出一個(gè)任務(wù)執(zhí)行
//如果隊(duì)列為空劲藐,則task還是null ,不進(jìn)入while循環(huán)。
while (task != null || (task = getTask()) != null) {
//加鎖
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())
//滿足上面的條件樟凄,則執(zhí)行中斷操作
wt.interrupt();
try {
//任務(wù)提交之前是否需要一些操作聘芜,交由子類實(shí)現(xiàn)
beforeExecute(wt, task);
Throwable thrown = null;
try {
task.run(); //執(zhí)行任務(wù),執(zhí)行Runnable中的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 {
//交給子類的實(shí)現(xiàn)方法
afterExecute(task, thrown);
}
} finally {
//修改task值缝龄,獲取新的任務(wù)執(zhí)行
task = null;
//完成任務(wù)數(shù)+1
w.completedTasks++;
//釋放鎖
w.unlock();
}
}
completedAbruptly = false;
} finally {
//清理線程的后續(xù)
processWorkerExit(w, completedAbruptly);
}
}
看一下上面方法的核心代碼汰现,while循環(huán)。如果傳入的對(duì)象不為null將直接運(yùn)行傳入的任務(wù)二拐,傳入任務(wù)完成之后服鹅,task變?yōu)閚ull, 則調(diào)用getTask()方法來獲取隊(duì)列中的任務(wù),如果隊(duì)列中一直有任務(wù)存在這又是一個(gè)死循環(huán)百新,while會(huì)一直循環(huán)下去企软,當(dāng)然這只是一種極端的情況,在getTask()方法有返回null的條件饭望,當(dāng)getTask()方法返回null,則退出runWorker()方法仗哨。
getTask()
private Runnable getTask() {
//線程等待超時(shí)變量形庭,默認(rèn)為false
boolean timedOut = false; // Did the last poll() time out?
//死循環(huán)
for (;;) {
//獲取ctl的值
int c = ctl.get();
//獲取線程池當(dāng)前的狀態(tài)值
int rs = runStateOf(c);
// Check if queue empty only if necessary.
//判斷線程池當(dāng)前所處的狀態(tài)
//線程的狀態(tài)不是RUNNING,隊(duì)列為空。
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
//工作線程的數(shù)量-1
decrementWorkerCount();
return null;
}
//工作線程的數(shù)量
int wc = workerCountOf(c);
// Are workers subject to culling?
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
//沒有超時(shí)厌漂,獲取一個(gè)任務(wù)
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
上面的getTask()方法內(nèi)部是一個(gè)死循環(huán)萨醒,結(jié)束死循環(huán)的話可以通過break或者return關(guān)鍵字。上面方法使用了return null 來結(jié)束任務(wù)獲取苇倡,分為以下情形富纸。
- 線程池狀態(tài) >= SHUTDOWN && (rs >= STOP 任務(wù)隊(duì)列為空)
- 線程獲取任務(wù)等待超時(shí)或者任務(wù)隊(duì)列為空了
- 成功取到了不為null的任務(wù)
關(guān)閉線程池
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
//線程池狀態(tài)設(shè)置為SHUTDOWN
advanceRunState(SHUTDOWN);
//中斷所有空閑線程,等待隊(duì)列任務(wù)執(zhí)行完成
interruptIdleWorkers();
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate();
}
可以通過調(diào)用線程池的shutdown或者shutdownNow方法來關(guān)閉線程池旨椒,它們的原理是遍歷池中的工作線程晓褪,然后逐個(gè)調(diào)用線程的interrupt方法來中斷線程,所以無法響應(yīng)中斷的任務(wù)可能永遠(yuǎn)無法終止综慎。shutdownNow首先將線程池的狀態(tài)設(shè)置為STOP,然后嘗試停止所有正在執(zhí)行的或者暫停的任務(wù)并返回等待執(zhí)行的任務(wù)列表涣仿。而shutdown只是將線程池的狀態(tài)設(shè)置成SHUTDOWN,然后中斷所有沒有在執(zhí)行的任務(wù)線程。
參考:
書籍:《JAVA并發(fā)編程藝術(shù) - 方騰飛》
博客: http://blog.csdn.net/clevergump/article/details/50688008