幾句閑扯:首先,我想說(shuō)java的線程池真的是很繞,以前一直都感覺(jué)新建幾個(gè)線程一直不退出到底是怎么實(shí)現(xiàn)的,也就有了后來(lái)學(xué)習(xí)ThreadPoolExecutor源碼。學(xué)習(xí)源碼的過(guò)程中,最惡心的其實(shí)就是幾種狀態(tài)的轉(zhuǎn)換了,這也是ThreadPoolExecutor的核心。花了將近小一周才大致的弄明白ThreadPoolExecutor的機(jī)制,遂記錄下來(lái)。
線程池有多重要#####
線程是一個(gè)程序員一定會(huì)涉及到的一個(gè)概念,但是線程的創(chuàng)建和切換都是代價(jià)比較大的。所以,我們有沒(méi)有一個(gè)好的方案能做到線程的復(fù)用呢?這就涉及到一個(gè)概念——線程池。合理的使用線程池能夠帶來(lái)3個(gè)很明顯的好處:
1.降低資源消耗:通過(guò)重用已經(jīng)創(chuàng)建的線程來(lái)降低線程創(chuàng)建和銷毀的消耗
2.提高響應(yīng)速度:任務(wù)到達(dá)時(shí)不需要等待線程創(chuàng)建就可以立即執(zhí)行。
3.提高線程的可管理性:線程池可以統(tǒng)一管理、分配、調(diào)優(yōu)和監(jiān)控。
java多線程池的支持——ThreadPoolExecutor#####
java的線程池支持主要通過(guò)ThreadPoolExecutor來(lái)實(shí)現(xiàn),我們使用的ExecutorService的各種線程池策略都是基于ThreadPoolExecutor實(shí)現(xiàn)的,所以ThreadPoolExecutor十分重要。要弄明白各種線程池策略,必須先弄明白ThreadPoolExecutor。
1. 實(shí)現(xiàn)原理#####
首先看一個(gè)線程池的流程圖:
step1.調(diào)用ThreadPoolExecutor的execute提交線程,首先檢查CorePool,如果CorePool內(nèi)的線程小于CorePoolSize,新創(chuàng)建線程執(zhí)行任務(wù)。
step2.如果當(dāng)前CorePool內(nèi)的線程大于等于CorePoolSize,那么將線程加入到BlockingQueue。
step3.如果不能加入BlockingQueue,在小于MaxPoolSize的情況下創(chuàng)建線程執(zhí)行任務(wù)。
step4.如果線程數(shù)大于等于MaxPoolSize,那么執(zhí)行拒絕策略。
2.線程池的創(chuàng)建#####
線程池的創(chuàng)建可以通過(guò)ThreadPoolExecutor的構(gòu)造方法實(shí)現(xiàn):
/**
* Creates a new {@code ThreadPoolExecutor} with the given initial
* parameters.
*
* @param corePoolSize the number of threads to keep in the pool, even
* if they are idle, unless {@code allowCoreThreadTimeOut} is set
* @param maximumPoolSize the maximum number of threads to allow in the
* pool
* @param keepAliveTime when the number of threads is greater than
* the core, this is the maximum time that excess idle threads
* will wait for new tasks before terminating.
* @param unit the time unit for the {@code keepAliveTime} argument
* @param workQueue the queue to use for holding tasks before they are
* executed. This queue will hold only the {@code Runnable}
* tasks submitted by the {@code execute} method.
* @param threadFactory the factory to use when the executor
* creates a new thread
* @param handler the handler to use when execution is blocked
* because the thread bounds and queue capacities are reached
* @throws IllegalArgumentException if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue}
* or {@code threadFactory} or {@code handler} is null
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
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;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
具體解釋一下上述參數(shù):
- corePoolSize 核心線程池大小
- maximumPoolSize 線程池最大容量大小
- keepAliveTime 線程池空閑時(shí),線程存活的時(shí)間
- TimeUnit 時(shí)間單位
- ThreadFactory 線程工廠
- BlockingQueue任務(wù)隊(duì)列
- RejectedExecutionHandler 線程拒絕策略
3.線程的提交#####
ThreadPoolExecutor的構(gòu)造方法如上所示,但是只是做一些參數(shù)的初始化,ThreadPoolExecutor被初始化好之后便可以提交線程任務(wù),線程的提交方法主要是execute和submit。這里主要說(shuō)execute,submit會(huì)在后續(xù)的博文中分析。
/**
* 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}.
*
* @param command the task to execute
* @throws RejectedExecutionException at discretion of
* {@code RejectedExecutionHandler}, if the task
* cannot be accepted for execution
* @throws NullPointerException if {@code command} is null
*/
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.
* 如果當(dāng)前的線程數(shù)小于核心線程池的大小,根據(jù)現(xiàn)有的線程作為第一個(gè)Worker運(yùn)行的線程,
* 新建一個(gè)Worker,addWorker自動(dòng)的檢查當(dāng)前線程池的狀態(tài)和Worker的數(shù)量,
* 防止線程池在不能添加線程的狀態(tài)下添加線程
*
* 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.
* 如果線程入隊(duì)成功,然后還是要進(jìn)行double-check的,因?yàn)榫€程池在入隊(duì)之后狀態(tài)是可能會(huì)發(fā)生變化的
*
* 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.
*
* 如果task不能入隊(duì)(隊(duì)列滿了),這時(shí)候嘗試增加一個(gè)新線程,如果增加失敗那么當(dāng)前的線程池狀態(tài)變化了或者線程池已經(jīng)滿了
* 然后拒絕task
*/
int c = ctl.get();
//當(dāng)前的Worker的數(shù)量小于核心線程池大小時(shí),新建一個(gè)Worker。
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))//recheck防止線程池狀態(tài)的突變,如果突變,那么將reject線程,防止workQueue中增加新線程
reject(command);
else if (workerCountOf(recheck) == 0)//上下兩個(gè)操作都有addWorker的操作,但是如果在workQueue.offer的時(shí)候Worker變?yōu)?,
//那么將沒(méi)有Worker執(zhí)行新的task,所以增加一個(gè)Worker.
addWorker(null, false);
}
//如果workQueue滿了,那么這時(shí)候可能還沒(méi)到線程池的maxnum,所以嘗試增加一個(gè)Worker
else if (!addWorker(command, false))
reject(command);//如果Worker數(shù)量到達(dá)上限,那么就拒絕此線程
}
這里需要明確幾個(gè)概念:
- Worker和Task的區(qū)別,Worker是當(dāng)前線程池中的線程,而task雖然是runnable,但是并沒(méi)有真正執(zhí)行,只是被Worker調(diào)用了run方法,后面會(huì)看到這部分的實(shí)現(xiàn)。
- maximumPoolSize和corePoolSize的區(qū)別:這個(gè)概念很重要,maximumPoolSize為線程池最大容量,也就是說(shuō)線程池最多能起多少Worker。corePoolSize是核心線程池的大小,當(dāng)corePoolSize滿了時(shí),同時(shí)workQueue full(ArrayBolckQueue是可能滿的) 那么此時(shí)允許新建Worker去處理workQueue中的Task,但是不能超過(guò)maximumPoolSize。超過(guò)corePoolSize之外的線程會(huì)在空閑超時(shí)后終止。
核心方法:addWorker#####
Worker的增加和Task的獲取以及終止都是在此方法中實(shí)現(xiàn)的,也就是這一個(gè)方法里面包含了很多東西。在addWorker方法中提到了Status的概念,Status是線程池的核心概念,這里我們先看一段關(guān)于status的注釋:
/**
* 首先ctl是一個(gè)原子量,同時(shí)它里面包含了兩個(gè)field,一個(gè)是workerCount,另一個(gè)是runState
* workerCount表示當(dāng)前有效的線程數(shù),也就是Worker的數(shù)量
* runState表示當(dāng)前線程池的狀態(tài)
* The main pool control state, ctl, is an atomic integer packing
* two conceptual fields
* workerCount, indicating the effective number of threads
* runState, indicating whether running, shutting down etc
*
* 兩者是怎么結(jié)合的呢?首先workerCount是占據(jù)著一個(gè)atomic integer的后29位的,而狀態(tài)占據(jù)了前3位
* 所以,workerCount上限是(2^29)-1。
* In order to pack them into one int, we limit workerCount to
* (2^29)-1 (about 500 million) threads rather than (2^31)-1 (2
* billion) otherwise representable. If this is ever an issue in
* the future, the variable can be changed to be an AtomicLong,
* and the shift/mask constants below adjusted. But until the need
* arises, this code is a bit faster and simpler using an int.
*
* The workerCount is the number of workers that have been
* permitted to start and not permitted to stop. The value may be
* transiently different from the actual number of live threads,
* for example when a ThreadFactory fails to create a thread when
* asked, and when exiting threads are still performing
* bookkeeping before terminating. The user-visible pool size is
* reported as the current size of the workers set.
*
* runState是整個(gè)線程池的運(yùn)行生命周期,有如下取值:
* 1. RUNNING:可以新加線程,同時(shí)可以處理queue中的線程。
* 2. SHUTDOWN:不增加新線程,但是處理queue中的線程。
* 3.STOP 不增加新線程,同時(shí)不處理queue中的線程。
* 4.TIDYING 所有的線程都終止了(queue中),同時(shí)workerCount為0,那么此時(shí)進(jìn)入TIDYING
* 5.terminated()方法結(jié)束,變?yōu)門ERMINATED
* The runState provides the main lifecyle control, taking on values:
*
* RUNNING: Accept new tasks and process queued tasks
* SHUTDOWN: Don't accept new tasks, but process queued tasks
* STOP: Don't accept new tasks, don't process queued tasks,
* and interrupt in-progress tasks
* TIDYING: All tasks have terminated, workerCount is zero,
* the thread transitioning to state TIDYING
* will run the terminated() hook method
* TERMINATED: terminated() has completed
*
* The numerical order among these values matters, to allow
* ordered comparisons. The runState monotonically increases over
* time, but need not hit each state. The transitions are:
* 狀態(tài)的轉(zhuǎn)化主要是:
* RUNNING -> SHUTDOWN(調(diào)用shutdown())
* On invocation of shutdown(), perhaps implicitly in finalize()
* (RUNNING or SHUTDOWN) -> STOP(調(diào)用shutdownNow())
* On invocation of shutdownNow()
* SHUTDOWN -> TIDYING(queue和pool均empty)
* When both queue and pool are empty
* STOP -> TIDYING(pool empty,此時(shí)queue已經(jīng)為empty)
* When pool is empty
* TIDYING -> TERMINATED(調(diào)用terminated())
* When the terminated() hook method has completed
*
* Threads waiting in awaitTermination() will return when the
* state reaches TERMINATED.
*
* Detecting the transition from SHUTDOWN to TIDYING is less
* straightforward than you'd like because the queue may become
* empty after non-empty and vice versa during SHUTDOWN state, but
* we can only terminate if, after seeing that it is empty, we see
* that workerCount is 0 (which sometimes entails a recheck -- see
* below).
*/
下面是狀態(tài)的代碼:
//利用ctl來(lái)保證當(dāng)前線程池的狀態(tài)和當(dāng)前的線程的數(shù)量。ps:低29位為線程池容量,高3位為線程狀態(tài)。
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
//設(shè)定偏移量
private static final int COUNT_BITS = Integer.SIZE - 3;
//確定最大的容量2^29-1
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
//幾個(gè)狀態(tài),用Integer的高三位表示
// runState is stored in the high-order bits
//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),取前三位
// Packing and unpacking ctl
private static int runStateOf(int c) { return c & ~CAPACITY; }
//獲取當(dāng)前正在工作的worker,主要是取后面29位
private static int workerCountOf(int c) { return c & CAPACITY; }
//獲取ctl
private static int ctlOf(int rs, int wc) { return rs | wc; }
接下來(lái)貼上addWorker方法看看:
/**
* Checks if a new worker can be added with respect to current
* pool state and the given bound (either core or maximum). If so,
* the worker count is adjusted accordingly, and, if possible, a
* new worker is created and started running firstTask as its
* first task. This method returns false if the pool is stopped or
* eligible to shut down. It also returns false if the thread
* factory fails to create a thread when asked, which requires a
* backout of workerCount, and a recheck for termination, in case
* the existence of this worker was holding up termination.
*
* @param firstTask the task the new thread should run first (or
* null if none). Workers are created with an initial first task
* (in method execute()) to bypass queuing when there are fewer
* than corePoolSize threads (in which case we always start one),
* or when the queue is full (in which case we must bypass queue).
* Initially idle threads are usually created via
* prestartCoreThread or to replace other dying workers.
*
* @param core if true use corePoolSize as bound, else
* maximumPoolSize. (A boolean indicator is used here rather than a
* value to ensure reads of fresh values after checking other pool
* state).
* @return true if successful
*/
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
/**
* rs!=Shutdown || fistTask!=null || workCount.isEmpty
* 如果當(dāng)前的線程池的狀態(tài)>SHUTDOWN 那么拒絕Worker的add 如果=SHUTDOWN
* 那么此時(shí)不能新加入不為null的Task,如果在WorkCount為empty的時(shí)候不能加入任何類型的Worker,
* 如果不為empty可以加入task為null的Worker,增加消費(fèi)的Worker
*/
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;
// else CAS failed due to workerCount change; retry inner loop
}
}
Worker w = new Worker(firstTask);
Thread t = w.thread;
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 c = ctl.get();
int rs = runStateOf(c);
/**
* rs!=SHUTDOWN ||firstTask!=null
*
* 同樣檢測(cè)當(dāng)rs>SHUTDOWN時(shí)直接拒絕減小Wc,同時(shí)Terminate,如果為SHUTDOWN同時(shí)firstTask不為null的時(shí)候也要Terminate
*/
if (t == null ||
(rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null))) {
decrementWorkerCount();
tryTerminate();
return false;
}
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
} finally {
mainLock.unlock();
}
t.start();
// It is possible (but unlikely) for a thread to have been
// added to workers, but not yet started, during transition to
// STOP, which could result in a rare missed interrupt,
// because Thread.interrupt is not guaranteed to have any effect
// on a non-yet-started Thread (see Thread#interrupt).
//Stop或線程Interrupt的時(shí)候要中止所有的運(yùn)行的Worker
if (runStateOf(ctl.get()) == STOP && ! t.isInterrupted())
t.interrupt();
return true;
}
addWorker中首先進(jìn)行了一次線程池狀態(tài)的檢測(cè):
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
//判斷當(dāng)前線程池的狀態(tài)是不是已經(jīng)shutdown,如果shutdown了拒絕線程加入
//(rs!=SHUTDOWN || first!=null || workQueue.isEmpty())
//如果rs不為SHUTDOWN,此時(shí)狀態(tài)是STOP、TIDYING或TERMINATED,所以此時(shí)要拒絕請(qǐng)求
//如果此時(shí)狀態(tài)為SHUTDOWN,而傳入一個(gè)不為null的線程,那么需要拒絕
//如果狀態(tài)為SHUTDOWN,同時(shí)隊(duì)列中已經(jīng)沒(méi)任務(wù)了,那么拒絕掉
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
其實(shí)是比較難懂的,主要在線程池狀態(tài)判斷條件這里:
- 如果是runing,那么跳過(guò)if。
- 如果rs>=SHUTDOWN,同時(shí)不等于SHUTDOWN,即為SHUTDOWN以上的狀態(tài),那么不接受新線程。
- 如果rs>=SHUTDOWN,同時(shí)等于SHUTDOWN,同時(shí)first!=null,那么拒絕新線程,如果first==null,那么可能是新增加線程消耗Queue中的線程。但是同時(shí)還要檢測(cè)workQueue是否isEmpty(),如果為Empty,那么隊(duì)列已空,不需要增加消耗線程,如果隊(duì)列沒(méi)有空那么運(yùn)行增加first=null的Worker。
從這里是可以看出一些策略的
首先,在rs>SHUTDOWN時(shí),拒絕一切線程的增加,因?yàn)镾TOP是會(huì)終止所有的線程,同時(shí)移除Queue中所有的待執(zhí)行的線程的,所以也不需要增加first=null的Worker了
其次,在SHUTDOWN狀態(tài)時(shí),是不能增加first!=null的Worker的,同時(shí)即使first=null,但是此時(shí)Queue為Empty也是不允許增加Worker的,SHUTDOWN下增加的Worker主要用于消耗Queue中的任務(wù)。
SHUTDOWN狀態(tài)時(shí),是不允許向workQueue中增加線程的,isRunning(c) && workQueue.offer(command) 每次在offer之前都要做狀態(tài)檢測(cè),也就是線程池狀態(tài)變?yōu)?gt;=SHUTDOWN時(shí)不允許新線程進(jìn)入線程池了。
for (;;) {
int wc = workerCountOf(c);
//如果當(dāng)前的數(shù)量超過(guò)了CAPACITY,或者超過(guò)了corePoolSize和maximumPoolSize(試core而定)
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
//CAS嘗試增加線程數(shù),如果失敗,證明有競(jìng)爭(zhēng),那么重新到retry。
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
//判斷當(dāng)前線程池的運(yùn)行狀態(tài)
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
這段代碼做了一個(gè)兼容,主要是沒(méi)有到corePoolSize 或maximumPoolSize上限時(shí),那么允許添加線程,CAS增加Worker的數(shù)量后,跳出循環(huán)。
接下來(lái)實(shí)例化Worker,實(shí)例化Worker其實(shí)是很關(guān)鍵的,后面會(huì)說(shuō)。
因?yàn)閣orkers是HashSet線程不安全的,那么此時(shí)需要加鎖,所以mainLock.lock(); 之后重新檢查線程池的狀態(tài),如果狀態(tài)不正確,那么減小Worker的數(shù)量,為什么tryTerminate()目前不大清楚。如果狀態(tài)正常,那么添加Worker到workers。最后:
if (runStateOf(ctl.get()) == STOP && ! t.isInterrupted())
t.interrupt();
注釋說(shuō)的很清楚,為了能及時(shí)的中斷此Worker,因?yàn)榫€程存在未Start的情況,此時(shí)是不能響應(yīng)中斷的,如果此時(shí)status變?yōu)镾TOP,則不能中斷線程。此處用作中斷線程之用。
接下來(lái)我們看Worker的方法:
/**
* Creates with given first task and thread from ThreadFactory.
* @param firstTask the first task (null if none)
*/
Worker(Runnable firstTask) {
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
這里可以看出Worker是對(duì)firstTask的包裝,并且Worker本身就是Runnable的,看上去真心很流氓的感覺(jué)~~~
通過(guò)ThreadFactory為Worker自己構(gòu)建一個(gè)線程。
因?yàn)閃orker是Runnable類型的,所以是有run方法的,上面也看到了會(huì)調(diào)用t.start() 其實(shí)就是執(zhí)行了run方法:
/** Delegates main run loop to outer runWorker */
public void run() {
runWorker(this);
}
調(diào)用了runWorker:
/**
* Main worker run loop. Repeatedly gets tasks from queue and
* executes them, while coping with a number of issues:
* 1 Worker可能還是執(zhí)行一個(gè)初始化的task——firstTask。
* 但是有時(shí)也不需要這個(gè)初始化的task(可以為null),只要pool在運(yùn)行,就會(huì)
* 通過(guò)getTask從隊(duì)列中獲取Task,如果返回null,那么worker退出。
* 另一種就是external拋出異常導(dǎo)致worker退出。
* 1. We may start out with an initial task, in which case we
* don't need to get the first one. Otherwise, as long as pool is
* running, we get tasks from getTask. If it returns null then the
* worker exits due to changed pool state or configuration
* parameters. Other exits result from exception throws in
* external code, in which case completedAbruptly holds, which
* usually leads processWorkerExit to replace this thread.
*
*
* 2 在運(yùn)行任何task之前,都需要對(duì)worker加鎖來(lái)防止other pool中斷worker。
* clearInterruptsForTaskRun保證除了線程池stop,那么現(xiàn)場(chǎng)都沒(méi)有中斷標(biāo)志
* 2. Before running any task, the lock is acquired to prevent
* other pool interrupts while the task is executing, and
* clearInterruptsForTaskRun called to ensure that unless pool is
* stopping, this thread does not have its interrupt set.
*
* 3. Each task run is preceded by a call to beforeExecute, which
* might throw an exception, in which case we cause thread to die
* (breaking loop with completedAbruptly true) without processing
* the task.
*
* 4. Assuming beforeExecute completes normally, we run the task,
* gathering any of its thrown exceptions to send to
* afterExecute. We separately handle RuntimeException, Error
* (both of which the specs guarantee that we trap) and arbitrary
* Throwables. Because we cannot rethrow Throwables within
* Runnable.run, we wrap them within Errors on the way out (to the
* thread's UncaughtExceptionHandler). Any thrown exception also
* conservatively causes thread to die.
*
* 5. After task.run completes, we call afterExecute, which may
* also throw an exception, which will also cause thread to
* die. According to JLS Sec 14.20, this exception is the one that
* will be in effect even if task.run throws.
*
* The net effect of the exception mechanics is that afterExecute
* and the thread's UncaughtExceptionHandler have as accurate
* information as we can provide about any problems encountered by
* user code.
*
* @param w the worker
*/
final void runWorker(Worker w) {
Runnable task = w.firstTask;
w.firstTask = null;
//標(biāo)識(shí)線程是不是異常終止的
boolean completedAbruptly = true;
try {
//task不為null情況是初始化worker時(shí),如果task為null,則去隊(duì)列中取線程--->getTask()
while (task != null || (task = getTask()) != null) {
w.lock();
//獲取woker的鎖,防止線程被其他線程中斷
clearInterruptsForTaskRun();//清楚所有中斷標(biāo)記
try {
beforeExecute(w.thread, task);//線程開(kāi)始執(zhí)行之前執(zhí)行此方法,可以實(shí)現(xiàn)Worker未執(zhí)行退出,本類中未實(shí)現(xiàn)
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);//線程執(zhí)行后執(zhí)行,可以實(shí)現(xiàn)標(biāo)識(shí)Worker異常中斷的功能,本類中未實(shí)現(xiàn)
}
} finally {
task = null;//運(yùn)行過(guò)的task標(biāo)null
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
//處理worker退出的邏輯
processWorkerExit(w, completedAbruptly);
}
}
從上面代碼可以看出,execute的Task是被“包裝 ”了一層,線程啟動(dòng)時(shí)是內(nèi)部調(diào)用了Task的run方法。
接下來(lái)所有的核心集中在getTask()方法上:
/**
* Performs blocking or timed wait for a task, depending on
* current configuration settings, or returns null if this worker
* must exit because of any of:
* 1. There are more than maximumPoolSize workers (due to
* a call to setMaximumPoolSize).
* 2. The pool is stopped.
* 3. The pool is shutdown and the queue is empty.
* 4. This worker timed out waiting for a task, and timed-out
* workers are subject to termination (that is,
* {@code allowCoreThreadTimeOut || workerCount > corePoolSize})
* both before and after the timed wait.
*
* @return task, or null if the worker must exit, in which case
* workerCount is decremented
*
*
* 隊(duì)列中獲取線程
*/
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
//當(dāng)前狀態(tài)為>stop時(shí),不處理workQueue中的任務(wù),同時(shí)減小worker的數(shù)量所以返回null,如果為shutdown 同時(shí)workQueue已經(jīng)empty了,同樣減小worker數(shù)量并返回null
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
boolean timed; // Are workers subject to culling?
for (;;) {
int wc = workerCountOf(c);
timed = allowCoreThreadTimeOut || wc > corePoolSize;
if (wc <= maximumPoolSize && ! (timedOut && timed))
break;
if (compareAndDecrementWorkerCount(c))
return null;
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
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)鍵,首先看幾個(gè)局部變量:
boolean timedOut = false;
主要是判斷后面的poll是否要超時(shí)
boolean timed;
主要是標(biāo)識(shí)著當(dāng)前Worker超時(shí)是否要退出。wc > corePoolSize時(shí)需要減小空閑的Worker數(shù),那么timed為true,但是wc <= corePoolSize時(shí),不能減小核心線程數(shù)timed為false。
timedOut初始為false,如果timed為true那么使用poll取線程。如果正常返回,那么返回取到的task。如果超時(shí),證明worker空閑,同時(shí)worker超過(guò)了corePoolSize,需要?jiǎng)h除。返回r=null。則 timedOut = true。此時(shí)循環(huán)到wc <= maximumPoolSize && ! (timedOut && timed)時(shí),減小worker數(shù),并返回null,導(dǎo)致worker退出。如果線程數(shù)<= corePoolSize,那么此時(shí)調(diào)用 workQueue.take(),沒(méi)有線程獲取到時(shí)將一直阻塞,知道獲取到線程或者中斷,關(guān)于中斷后面Shutdown的時(shí)候會(huì)說(shuō)。
至此線程執(zhí)行過(guò)程就分析完了~~~~
關(guān)于終止線程池#####
我個(gè)人認(rèn)為,如果想了解明白線程池,那么就一定要理解好各個(gè)狀態(tài)之間的轉(zhuǎn)換,想理解轉(zhuǎn)換,線程池的終止機(jī)制是很好的一個(gè)途徑。對(duì)于關(guān)閉線程池主要有兩個(gè)方法shutdown()和shutdownNow():
首先從shutdown()方法開(kāi)始:
/**
* Initiates an orderly shutdown in which previously submitted
* tasks are executed, but no new tasks will be accepted.
* Invocation has no additional effect if already shut down.
*
* <p>This method does not wait for previously submitted tasks to
* complete execution. Use {@link #awaitTermination awaitTermination}
* to do that.
*
* @throws SecurityException {@inheritDoc}
*/
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//判斷是否可以操作目標(biāo)線程
checkShutdownAccess();
//設(shè)置線程池狀態(tài)為SHUTDOWN,此處之后,線程池中不會(huì)增加新Task
advanceRunState(SHUTDOWN);
//中斷所有的空閑線程
interruptIdleWorkers();
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
//轉(zhuǎn)到Terminate
tryTerminate();
}
shutdown做了幾件事:
1. 檢查是否能操作目標(biāo)線程
2. 將線程池狀態(tài)轉(zhuǎn)為SHUTDOWN
3. 中斷所有空閑線程
這里就引發(fā)了一個(gè)問(wèn)題,什么是空閑線程?
這需要接著看看interruptIdleWorkers是怎么回事。
private void interruptIdleWorkers(boolean onlyOne) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
//這里的意圖很簡(jiǎn)單,遍歷workers 對(duì)所有worker做中斷處理。
// w.tryLock()對(duì)Worker加鎖,這保證了正在運(yùn)行執(zhí)行Task的Worker不會(huì)被中斷,那么能中斷哪些線程呢?
try {
for (Worker w : workers) {
Thread t = w.thread;
if (!t.isInterrupted() && w.tryLock()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
if (onlyOne)
break;
}
} finally {
mainLock.unlock();
}
}
這里主要是為了中斷worker,但是中斷之前需要先獲取鎖,這就意味著正在運(yùn)行的Worker不能中斷。但是上面的代碼有w.tryLock(),那么獲取不到鎖就不會(huì)中斷,shutdown的Interrupt只是對(duì)所有的空閑Worker(正在從workQueue中取Task,此時(shí)Worker沒(méi)有加鎖)發(fā)送中斷信號(hào)。
while (task != null || (task = getTask()) != null) {
w.lock();
//獲取woker的鎖,防止線程被其他線程中斷
clearInterruptsForTaskRun();//清楚所有中斷標(biāo)記
try {
beforeExecute(w.thread, task);//線程開(kāi)始執(zhí)行之前執(zhí)行此方法,可以實(shí)現(xiàn)Worker未執(zhí)行退出,本類中未實(shí)現(xiàn)
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);//線程執(zhí)行后執(zhí)行,可以實(shí)現(xiàn)標(biāo)識(shí)Worker異常中斷的功能,本類中未實(shí)現(xiàn)
}
} finally {
task = null;//運(yùn)行過(guò)的task標(biāo)null
w.completedTasks++;
w.unlock();
}
}
在runWorker中,每一個(gè)Worker getTask成功之后都要獲取Worker的鎖之后運(yùn)行,也就是說(shuō)運(yùn)行中的Worker不會(huì)中斷。因?yàn)楹诵木€程一般在空閑的時(shí)候會(huì)一直阻塞在獲取Task上,也只有中斷才可能導(dǎo)致其退出。這些阻塞著的Worker就是空閑的線程(當(dāng)然,非核心線程,并且阻塞的也是空閑線程)。在getTask方法中:
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
//當(dāng)前狀態(tài)為>stop時(shí),不處理workQueue中的任務(wù),同時(shí)減小worker的數(shù)量所以返回null,如果為shutdown 同時(shí)workQueue已經(jīng)empty了,同樣減小worker數(shù)量并返回null
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
boolean timed; // Are workers subject to culling?
for (;;) {
//allowCoreThreadTimeOu是判斷CoreThread是否會(huì)超時(shí)的,true為會(huì)超時(shí),false不會(huì)超時(shí)。默認(rèn)為false
int wc = workerCountOf(c);
timed = allowCoreThreadTimeOut || wc > corePoolSize;
if (wc <= maximumPoolSize && ! (timedOut && timed))
break;
if (compareAndDecrementWorkerCount(c))
return null;
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
會(huì)有兩階段的Worker:
- 剛進(jìn)入getTask(),還沒(méi)進(jìn)行狀態(tài)判斷。
- block在poll或者take上的Worker。
當(dāng)調(diào)用ShutDown方法時(shí),首先設(shè)置了線程池的狀態(tài)為ShutDown,此時(shí)1階段的worker進(jìn)入到狀態(tài)判斷時(shí)會(huì)返回null,此時(shí)Worker退出。
因?yàn)間etTask的時(shí)候是不加鎖的,所以在shutdown時(shí)可以調(diào)用worker.Interrupt.此時(shí)會(huì)中斷退出,Loop到狀態(tài)判斷時(shí),同時(shí)workQueue為empty。那么拋出中斷異常,導(dǎo)致重新Loop,在檢測(cè)線程池狀態(tài)時(shí),Worker退出。如果workQueue不為null就不會(huì)退出,此處有些疑問(wèn),因?yàn)闆](méi)有看見(jiàn)中斷標(biāo)志位清除的邏輯,那么這里就會(huì)不停的循環(huán)直到workQueue為Empty退出。
這里也能看出來(lái)SHUTDOWN只是清除一些空閑Worker,并且拒絕新Task加入,對(duì)于workQueue中的線程還是繼續(xù)處理的。
對(duì)于shutdown中獲取mainLock而addWorker中也做了mainLock的獲取,這么做主要是因?yàn)閃orks是HashSet類型的,是線程不安全的,我們也看到在addWorker后面也是對(duì)線程池狀態(tài)做了判斷,將Worker添加和中斷邏輯分離開(kāi)。
接下來(lái)做了tryTerminate()操作,這操作是進(jìn)行了后面狀態(tài)的轉(zhuǎn)換,在shutdownNow后面說(shuō)。
接下來(lái)看看shutdownNow:
/**
* Attempts to stop all actively executing tasks, halts the
* processing of waiting tasks, and returns a list of the tasks
* that were awaiting execution. These tasks are drained (removed)
* from the task queue upon return from this method.
*
* <p>This method does not wait for actively executing tasks to
* terminate. Use {@link #awaitTermination awaitTermination} to
* do that.
*
* <p>There are no guarantees beyond best-effort attempts to stop
* processing actively executing tasks. This implementation
* cancels tasks via {@link Thread#interrupt}, so any task that
* fails to respond to interrupts may never terminate.
*
* @throws SecurityException {@inheritDoc}
*/
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(STOP);
interruptWorkers();
tasks = drainQueue();
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
shutdownNow和shutdown代碼類似,但是實(shí)現(xiàn)卻很不相同。首先是設(shè)置線程池狀態(tài)為STOP,前面的代碼我們可以看到,是對(duì)SHUTDOWN有一些額外的判斷邏輯,但是對(duì)于>=STOP,基本都是reject,STOP也是比SHUTDOWN更加嚴(yán)格的一種狀態(tài)。此時(shí)不會(huì)有新Worker加入,所有剛執(zhí)行完一個(gè)線程后去GetTask的Worker都會(huì)退出。
之后調(diào)用interruptWorkers:
/**
* Interrupts all threads, even if active. Ignores SecurityExceptions
* (in which case some threads may remain uninterrupted).
*/
private void interruptWorkers() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers) {
try {
w.thread.interrupt();
} catch (SecurityException ignore) {
}
}
} finally {
mainLock.unlock();
}
}
這里可以看出來(lái),此方法目的是中斷所有的Worker,而不是像shutdown中那樣只中斷空閑線程。這樣體現(xiàn)了STOP的特點(diǎn),中斷所有線程,同時(shí)workQueue中的Task也不會(huì)執(zhí)行了。所以接下來(lái)drainQueue:
/**
* Drains the task queue into a new list, normally using
* drainTo. But if the queue is a DelayQueue or any other kind of
* queue for which poll or drainTo may fail to remove some
* elements, it deletes them one by one.
*/
private List<Runnable> drainQueue() {
BlockingQueue<Runnable> q = workQueue;
List<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;
}
獲取所有沒(méi)有執(zhí)行的Task,并且返回。
這也體現(xiàn)了STOP的特點(diǎn):
拒絕所有新Task的加入,同時(shí)中斷所有線程,WorkerQueue中沒(méi)有執(zhí)行的線程全部拋棄。所以此時(shí)Pool是空的,WorkerQueue也是空的。
這之后就是進(jìn)行到TIDYING和TERMINATED的轉(zhuǎn)化了:
/**
* Transitions to TERMINATED state if either (SHUTDOWN and pool
* and queue empty) or (STOP and pool empty). If otherwise
* eligible to terminate but workerCount is nonzero, interrupts an
* idle worker to ensure that shutdown signals propagate. This
* method must be called following any action that might make
* termination possible -- reducing worker count or removing tasks
* from the queue during shutdown. The method is non-private to
* allow access from ScheduledThreadPoolExecutor.
*/
final void tryTerminate() {
for (;;) {
int c = ctl.get();
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return;
if (workerCountOf(c) != 0) { // Eligible to terminate
interruptIdleWorkers(ONLY_ONE);
return;
}
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
terminated();
} finally {
ctl.set(ctlOf(TERMINATED, 0));
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
// else retry on failed CAS
}
}
上面的代碼其實(shí)很有意思有幾種狀態(tài)是不能轉(zhuǎn)化到TIDYING的:
- RUNNING狀態(tài)
- TIDYING或TERMINATED
- SHUTDOWN狀態(tài),但是workQueue不為空
也說(shuō)明了兩點(diǎn):
1. SHUTDOWN想轉(zhuǎn)化為TIDYING,需要workQueue為空,同時(shí)workerCount為0。
2. STOP轉(zhuǎn)化為TIDYING,需要workerCount為0
如果滿足上面的條件(一般一定時(shí)間后都會(huì)滿足的),那么CAS成TIDYING,TIDYING也只是個(gè)過(guò)度狀態(tài),最終會(huì)轉(zhuǎn)化為TERMINATED。
至此,ThreadPoolExecutor一些核心思想就介紹完了,想分析清楚實(shí)在是不容易,對(duì)于ThreadPoolExecutor我還是有些不懂地方,以上只是我對(duì)源碼的片面的見(jiàn)解,如果有不正確之處,希望大神能不吝賜教。同時(shí)也希望給正在研究ThreadPoolExecutor的童鞋提供一點(diǎn)幫助。
勿忘初心,方得始終。晚安~~