一、為何要使用線(xiàn)程池
在Java中,要使用多線(xiàn)程,除了使用new Thread()之外,還可以使用線(xiàn)程池ExecutorService。
// 使用Thread
Thread t = new Thread(new Runnable() {
@Override
public void run() {
// ...
}
});
t.start();
// 使用線(xiàn)程池
ExecutorService es = Executors.newSingleThreadExecutor();
es.execute(new Runnable() {
@Override
public void run() {
// ...
}
});
線(xiàn)程池主要解決了兩個(gè)問(wèn)題:
- 頻繁創(chuàng)建銷(xiāo)毀線(xiàn)程的開(kāi)銷(xiāo)
- 任務(wù)的管理
在異步任務(wù)比較多時(shí),創(chuàng)建、銷(xiāo)毀線(xiàn)程會(huì)占用很多系統(tǒng)資源;這時(shí)候,使用線(xiàn)程池,就可以實(shí)現(xiàn)線(xiàn)程的復(fù)用,讓人專(zhuān)注于任務(wù)的實(shí)現(xiàn),而不是管理線(xiàn)程。
二、線(xiàn)程池簡(jiǎn)介
1. 什么是線(xiàn)程池
線(xiàn)程池(本文特指ThreadPoolExecutor類(lèi))顧名思義,就是一個(gè)裝了線(xiàn)程的池子。線(xiàn)程池創(chuàng)建和管理若干線(xiàn)程,在需要使用的時(shí)候可以直接從線(xiàn)程池中取出來(lái)使用,在任務(wù)結(jié)束之后閑置等待復(fù)用,或者銷(xiāo)毀。
線(xiàn)程池中的線(xiàn)程分為兩種:核心線(xiàn)程和普通線(xiàn)程。核心線(xiàn)程即線(xiàn)程池中長(zhǎng)期存活的線(xiàn)程,即使閑置下來(lái)也不會(huì)被銷(xiāo)毀,需要使用的時(shí)候可以直接拿來(lái)用。而普通線(xiàn)程則有一定的壽命,如果閑置時(shí)間超過(guò)壽命,則這個(gè)線(xiàn)程就會(huì)被銷(xiāo)毀。
查看ThreadPoolExecutor類(lèi)的其中一個(gè)典型的構(gòu)造方法:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
線(xiàn)程池的具體行為和幾個(gè)參數(shù)有關(guān):
-
核心數(shù) corePoolSize
線(xiàn)程池中核心線(xiàn)程的數(shù)量。 -
最大容量 maximumPoolSize
線(xiàn)程池最大允許保留多少線(xiàn)程。 -
超時(shí)時(shí)間 keepAliveTime
線(xiàn)程池中普通線(xiàn)程的存活時(shí)間。
2. 線(xiàn)程池的使用
線(xiàn)程池的一般使用步驟如下:
- 使用
Executors中的工廠(chǎng)方法來(lái)獲取ExecutorService實(shí)例; - 使用
ExecutorService的execute(runnable)或者submit(runnable)方法來(lái)添加任務(wù)。
ExecutorService es = Executors.newSingleThreadExecutor();
es.execute(new Runnable() {
@Override
public void run() {
String response = new HttpUtil().get("http://littlefogcat.top");
System.out.println(response);
}
});
3. 線(xiàn)程池的分類(lèi)
在Executors工廠(chǎng)類(lèi)中提供了多種線(xiàn)程池,典型的有以下四種:
1. SingleThreadExecutor 單線(xiàn)程線(xiàn)程池
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
核心線(xiàn)程數(shù)為1,最大線(xiàn)程數(shù)為1,也就是說(shuō)SingleThreadExecutor這個(gè)線(xiàn)程池中的線(xiàn)程數(shù)固定為1。使用場(chǎng)景:當(dāng)多個(gè)任務(wù)都需要訪(fǎng)問(wèn)同一個(gè)資源的時(shí)候。
2. FixedThreadPool 固定容量線(xiàn)程池
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
核心線(xiàn)程數(shù)為n,最大線(xiàn)程數(shù)為n。使用場(chǎng)景:明確同時(shí)執(zhí)行任務(wù)數(shù)量時(shí)。
3. CachedThreadPool 緩存線(xiàn)程池
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
核心線(xiàn)程數(shù)為0,最大線(xiàn)程數(shù)無(wú)上限,線(xiàn)程超時(shí)時(shí)間60秒。使用場(chǎng)景:處理大量耗時(shí)較短的任務(wù)。
4. ScheduledThreadPool 定時(shí)線(xiàn)程池
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
/*
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
*/
核心線(xiàn)程數(shù)自定,最大線(xiàn)程數(shù)無(wú)上限。使用場(chǎng)景:處理延時(shí)任務(wù)。
可以看到,這四個(gè)方法都返回了一個(gè)ThreadPoolExecutor對(duì)象(ScheduledThreadPoolExecutor是其子類(lèi)),僅僅是其中的參數(shù)略有不同。所以接下來(lái)就對(duì)ThreadPoolExecutor類(lèi)進(jìn)行解析。
我將這四種常見(jiàn)的線(xiàn)程池總結(jié)了一個(gè)表格:

三、線(xiàn)程池的工作流程
1. 典型的線(xiàn)程池使用方式
一個(gè)典型的線(xiàn)程池使用方式如下:
ExecutorService exec = Executors.newCachedThreadPool();
exec.execute(new Runnable() {
@Override
public void run() {
// do sth
}
});
這里就以ThreadPoolExecutor.execute(runnable)方法切入,分析線(xiàn)程池的工作流程。
在ThreadPoolExecutor.execute(runnable)方法的注釋中寫(xiě)道:
Executes the given task sometime in the future. The task
may execute in a new thread or in an existing pooled thread.
If the task cannot be submitted for execution, either because this
executor has been shutdown or because its capacity has been reached,
the task is handled by the current {@code RejectedExecutionHandler}.
簡(jiǎn)單來(lái)說(shuō)就是將這個(gè)傳入的runnable對(duì)象提交到線(xiàn)程池中,等待執(zhí)行;如果線(xiàn)程池關(guān)閉,或者容量到上限不可以執(zhí)行了,那么就無(wú)法提交,會(huì)交給線(xiàn)程池的RejectedExecutionHandler進(jìn)行處理(這個(gè)RejectedExecutionHandler在構(gòu)造方法中傳入,或者通過(guò)setRejectedExecutionHandler(handler)方法指定)。
2. 線(xiàn)程池工作流程
線(xiàn)程池的工作流程還是比較清晰的,具體的源碼分析在第四節(jié)中,本節(jié)只做簡(jiǎn)要說(shuō)明。
2.1 添加任務(wù)
當(dāng)調(diào)用ThreadPoolExecutor.execute(runnable)的時(shí)候,會(huì)進(jìn)行以下判斷(這里不考慮延時(shí)任務(wù)):
- 如果線(xiàn)程池中,運(yùn)行的線(xiàn)程數(shù)少于核心線(xiàn)程數(shù)(corePoolSize),那么就新建一個(gè)線(xiàn)程,并執(zhí)行該任務(wù)。
- 如果線(xiàn)程池中,運(yùn)行的線(xiàn)程數(shù)大于等于corePoolSize,將線(xiàn)程添加到待執(zhí)行隊(duì)列中,等待執(zhí)行;
- 如果2中添加到隊(duì)列失敗,那么就新建一個(gè)非核心線(xiàn)程,并在該線(xiàn)程執(zhí)行該任務(wù);
- 如果當(dāng)前線(xiàn)程數(shù)已經(jīng)達(dá)到最大線(xiàn)程數(shù)(maximumPoolSize),那么拒絕這個(gè)任務(wù)。
這里有個(gè)問(wèn)題,什么情況下,任務(wù)會(huì)添加失敗呢?這個(gè)問(wèn)題會(huì)在下面第四節(jié)源碼分析中workQueue部分說(shuō)明。
2.2 執(zhí)行任務(wù)
在2.1添加任務(wù)中,添加失敗自然不必執(zhí)行,會(huì)直接拒絕任務(wù);任務(wù)添加成功有兩種情況:
- 將任務(wù)添加到任務(wù)隊(duì)列;
- 新建線(xiàn)程執(zhí)行任務(wù)。
新建線(xiàn)程自不必說(shuō),主要看看添加到任務(wù)隊(duì)列中的任務(wù)是如何被執(zhí)行的。
從2.1中我們知道,每一個(gè)工作線(xiàn)程必然是被一個(gè)任務(wù)喚醒的,這個(gè)任務(wù)被稱(chēng)作初始任務(wù)(firstTask)。當(dāng)一個(gè)工作線(xiàn)程完了它的初始任務(wù)之后,會(huì)從待執(zhí)行的任務(wù)隊(duì)列(workQueue)中取新的任務(wù)。workQueue是一個(gè)阻塞隊(duì)列,線(xiàn)程會(huì)一直等待直到有新的任務(wù)到來(lái)為止。對(duì)于一個(gè)設(shè)置了超時(shí)時(shí)間的線(xiàn)程,如果在指定的時(shí)間之后仍然沒(méi)有新任務(wù)到達(dá),那么這個(gè)線(xiàn)程就會(huì)停止等待任務(wù)并且銷(xiāo)毀。
四、線(xiàn)程池中的一些重要概念
Worker / workers
Worker類(lèi)是ThreadPoolExecutor類(lèi)的一個(gè)內(nèi)部類(lèi),也是線(xiàn)程池管理操作線(xiàn)程的核心所在。每一個(gè)worker都對(duì)應(yīng)著一個(gè)thread,所以在不混淆的情況下,可以把worker理解為工作線(xiàn)程。
ThreadPoolExecutor有一個(gè)名為workers的成員變量,它保存了這個(gè)線(xiàn)程池所有存活的worker對(duì)象。
workQueue
workQueue是線(xiàn)程池內(nèi)部用來(lái)保存待執(zhí)行任務(wù)的隊(duì)列。它是一個(gè)BlockingQueue<Runnable>類(lèi)型的變量,在沒(méi)有任務(wù)的時(shí)候,它的poll()方法會(huì)阻塞。
在一個(gè)允許超時(shí)的worker執(zhí)行完任務(wù)之后,會(huì)調(diào)用workQueue.poll()取出下一個(gè)任務(wù)執(zhí)行。如果沒(méi)有任務(wù),則會(huì)在這里阻塞;當(dāng)阻塞時(shí)間達(dá)到超時(shí)時(shí)間后,這個(gè)工作線(xiàn)程會(huì)退出并銷(xiāo)毀。
五、通過(guò)源碼詳細(xì)分析線(xiàn)程池
1. ctl
ThreadPoolExecutor通過(guò)一個(gè)原子整型ctl來(lái)保存線(xiàn)程池的兩個(gè)重要字段,workerCount和runState。workerCount即線(xiàn)程池工作線(xiàn)程的數(shù)量,而runState代表了線(xiàn)程池當(dāng)前的狀態(tài)(如:運(yùn)行中、關(guān)閉、終止)。通過(guò)位運(yùn)算,可以從ctl得到workerCount和runState的值,反之也可以通過(guò)workerCount和runState組合得到ctl。
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static int runStateOf(int c) { return c & ~CAPACITY; }
private static int workerCountOf(int c) { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
顯然,這跟Android中的MesureSpec通過(guò)一個(gè)整數(shù)來(lái)保存兩個(gè)屬性原理是相同的。
2. execute(runnable)方法
本節(jié)所有流程都以ThreadPoolExecutor.execute(runnable)方法切入,分析線(xiàn)程池的源碼:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
reject(command);
}
可以看到,這個(gè)方法很簡(jiǎn)單,正如三-2.1小節(jié)所說(shuō)的一樣,在添加任務(wù)時(shí)做一些判斷。在ThreadPoolExecutor中,有一個(gè)隊(duì)列workQueue保存了待執(zhí)行的任務(wù)。而當(dāng)需要新建線(xiàn)程的時(shí)候,則執(zhí)行addWorker(runnable, core)方法來(lái)創(chuàng)建一個(gè)worker/線(xiàn)程。
因?yàn)檫@個(gè)方法是線(xiàn)程池執(zhí)行的核心,所以下面重點(diǎn)理解這個(gè)方法里面的語(yǔ)句。
3. workQueue / Worker
workQueue是ThreadPoolExecutor類(lèi)的一個(gè)非常重要的成員變量。在2中,我們知道了,當(dāng)正在執(zhí)行的線(xiàn)程數(shù)量大于核心線(xiàn)程數(shù),那么會(huì)優(yōu)先將任務(wù)添加到任務(wù)隊(duì)列,即workQueue中。
通過(guò)execute(runnable)方法可以知道,對(duì)于一個(gè)處在運(yùn)行中的線(xiàn)程池,只有在當(dāng)前工作線(xiàn)程數(shù)量大于等于核心數(shù)時(shí),才會(huì)將任務(wù)往隊(duì)列中添加。并且,如果往任務(wù)隊(duì)列添加失敗的話(huà),就會(huì)開(kāi)啟新的工作線(xiàn)程。
那么回到第三節(jié)中的問(wèn)題,什么情況下會(huì)添加失敗呢?注意這一句:
if (isRunning(c) && workQueue.offer(command)) {
// ...
}
很簡(jiǎn)單,當(dāng)workQueue.offer(command)返回false的時(shí)候,則說(shuō)明添加失敗。一般來(lái)說(shuō),當(dāng)隊(duì)列的容量滿(mǎn)了,offer方法就會(huì)返回false。即,在線(xiàn)程數(shù)超過(guò)了核心數(shù)(workerCount>corePoolSize)的情況下,只有在任務(wù)隊(duì)列被填滿(mǎn)之后,線(xiàn)程池才會(huì)考慮創(chuàng)建新線(xiàn)程,否則只會(huì)將任務(wù)添加到任務(wù)隊(duì)列中等待執(zhí)行。在線(xiàn)程池的構(gòu)造方法中傳入不同的隊(duì)列類(lèi)型,就會(huì)有不同的效果?;氐?code>Executors工廠(chǎng)類(lèi)中,看看四種基本的線(xiàn)程池分別都是使用的什么隊(duì)列?
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
// ScheduledThreadPoolExecutor的構(gòu)造方法
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
SingleThreadExecutor
核心數(shù)和最大線(xiàn)程數(shù)均為1,使用LinkedBlockingQueue,容量為Interger.MAX_VALUE。也就是說(shuō),SingleThreadExecutor中永遠(yuǎn)只有一個(gè)線(xiàn)程,所有任務(wù)單線(xiàn)執(zhí)行,并且容量無(wú)上限。CachedThreadPool
核心數(shù)為0,最大線(xiàn)程數(shù)Interger.MAX_VALUE,使用SynchronousQueue。這個(gè)隊(duì)列的特點(diǎn)是,沒(méi)有內(nèi)部容量。也就是說(shuō),對(duì)于一個(gè)新任務(wù),但凡是沒(méi)有空閑的線(xiàn)程,那么就創(chuàng)建一個(gè)新的線(xiàn)程。而由于核心數(shù)是0,當(dāng)超過(guò)一定時(shí)間沒(méi)有新任務(wù)之后,線(xiàn)程池中所有線(xiàn)程都將被銷(xiāo)毀。FixedThreadPool
和SingleThreadExecutor類(lèi)似,使用LinkedBlockingQueue;不同的是核心數(shù)和最大線(xiàn)程數(shù)為n。ScheduledThreadPoolExecutor
使用DelayedWorkQueue,可以實(shí)現(xiàn)延時(shí)/定時(shí)獲取任務(wù)。
看完這里,就能很好的理解Executors中的這些線(xiàn)程池為何能夠呈現(xiàn)出各自的特性了。
在第四節(jié)中我們知道,對(duì)于線(xiàn)程的操作等,不是直接通過(guò)Thread來(lái)進(jìn)行的,而一般是通過(guò)Worker類(lèi)進(jìn)行。每一個(gè)Worker對(duì)應(yīng)了一個(gè)線(xiàn)程,任務(wù)的添加、執(zhí)行等,都是通過(guò)Worker來(lái)實(shí)現(xiàn)的。ThreadPoolExecutor中有一個(gè)HashSet<Worker>類(lèi)型的變量workers,用來(lái)保存可用的Worker。也就是說(shuō),我們所謂的“線(xiàn)程池”實(shí)際本質(zhì)上就是“Worker池”。由于Worker和Thread是一對(duì)一的關(guān)系,所以為了圖方便,有時(shí)候可以簡(jiǎn)單的把Worker理解成一個(gè)工作線(xiàn)程,但需要知道其本質(zhì)上與真正的線(xiàn)程Thread是不同的。
Worker類(lèi)是ThreadPoolExecutor的一個(gè)內(nèi)部類(lèi),繼承自AbstractQueuedSynchronizer,實(shí)現(xiàn)了Runnable接口:
// ...略去一部分
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
final Thread thread;
Runnable firstTask;
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
public void run() {
runWorker(this);
}
}
真實(shí)運(yùn)行的線(xiàn)程,是worker.thread,在Worker構(gòu)造方法中,worker.thread通過(guò)工廠(chǎng)方法創(chuàng)建。而線(xiàn)程肯定是要調(diào)用start()方法運(yùn)行的,搜索一下worker.thread的start()方法,發(fā)現(xiàn)是在ThreadPoolExecutor.addWorker()這個(gè)方法里調(diào)用的。
在下面的第4小節(jié)中,會(huì)專(zhuān)門(mén)分析這個(gè)addWorker(runnable, core)方法。
另一方面,Worker本質(zhì)上又是一個(gè)Runnable對(duì)象,是一個(gè)可運(yùn)行任務(wù),在真實(shí)線(xiàn)程worker.thread啟動(dòng)后,會(huì)調(diào)用其run()方法:
// Worker中
public void run() {
runWorker(this);
}
4. addWorker(runnable, boolean)方法
線(xiàn)程池創(chuàng)建工作線(xiàn)程是通過(guò)addWorker方法來(lái)進(jìn)行的。
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
int wc = workerCountOf(c);
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
continue retry;
}
}
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;
mainLock.lock();
try {
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
雖然這段代碼有點(diǎn)長(zhǎng),但是所做的事情其實(shí)只有兩件:
- 檢查是否應(yīng)該添加這個(gè)worker:只有在線(xiàn)程池處于正在運(yùn)行的狀態(tài)(runState==RUNNING),并且當(dāng)前worker數(shù)小于最大容量時(shí),才能添加;
- 新建worker對(duì)象并添加到workers中。
5. runWorker(Worker)方法
在3中我們得知,當(dāng)Worker的線(xiàn)程開(kāi)始運(yùn)行之后,會(huì)調(diào)用其run()方法:
// Worker中
public void run() {
runWorker(this);
}
而run()又會(huì)調(diào)用ThreadPoolExecutor.runWorker(Worker)方法。在這里看一下這個(gè)方法。
// 省略一大部分
final void runWorker(Worker w) {
Runnable task = w.firstTask;
while (task != null || (task = getTask()) != null) {
try {
task.run();
} catch (Exception x) {
}
}
}
// 省略一大部分
private Runnable getTask() {
for (;;) {
if (/*無(wú)法獲取任務(wù)*/) {
return null;
}
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
} catch (InterruptedException retry) {
}
}
}
為了便于觀(guān)看,我刪去了大部分代碼,只留了核心的幾行??梢钥吹?,在Worker的任務(wù)執(zhí)行完畢之后,會(huì)再?gòu)膚orkQueue隊(duì)列中獲取新的任務(wù),按此無(wú)限循環(huán)。什么時(shí)候Worker會(huì)結(jié)束并銷(xiāo)毀呢?從這一句while (task != null || (task = getTask()) != null)中,即worker中沒(méi)有任務(wù),并且getTast()返回null,worker就會(huì)結(jié)束執(zhí)行。什么時(shí)候返回null,不讓worker繼續(xù)存活了呢?
- 線(xiàn)程池被shutdown,并且任務(wù)隊(duì)列空了;
- 線(xiàn)程池超容量;
- 超時(shí);
也就是說(shuō),如果線(xiàn)程池在運(yùn)行狀態(tài),容量也沒(méi)有到最大,并且任務(wù)隊(duì)列還有任務(wù),這個(gè)worker就會(huì)永遠(yuǎn)運(yùn)行下去。
六、總結(jié)
就用圖片來(lái)總結(jié)一下。
下圖闡述了線(xiàn)程池調(diào)用execute(runnable)之后的流程。

這張圖表示了execute之后的調(diào)用鏈,相當(dāng)于Worker的生命周期了(不包括銷(xiāo)毀)。
