下面我們來看一看它的工作原理。
線程池的主要狀態(tài)由一個(gè)AtomicInteger
變量ctl控制,里面存放了兩個(gè)概念變量:
- workerCount 表示有效線程的數(shù)量。
- runState 表示線程池是否在運(yùn)行或是正在關(guān)閉等狀態(tài)。
<b>workerCount:</b>
為了把這兩個(gè)變量包裝到同一個(gè)int中,ThreadPoolExecutor限制了workerCount最大值是2^29-1。workerCount是允許開發(fā)和未允許關(guān)閉的線程數(shù)之和。這個(gè)數(shù)字可能在短時(shí)間與實(shí)際存活的線程數(shù)不同。比如在ThreadFactory
創(chuàng)建線程失敗,或是將要退出的線程正在保存狀態(tài)信息時(shí),workerCount將為用戶可見的線程池大小。比如添加線程失敗,有可能還沒來得及將線程的數(shù)量-1。此時(shí)取到的線程數(shù),就是存活的線程數(shù)+1。
<b>runState :</b>
runState則提供了線程池生命周期的控制。它的狀態(tài)包括:
- RUNNING: 接受新的任務(wù),并處理排隊(duì)中的任務(wù)。
- SHUTDOWN:不再接受新的任務(wù),但還是會處理排隊(duì)的任務(wù)。
- STOP:不再接受新的任務(wù),不處理正在排隊(duì)的任務(wù),并且會打斷正在處理的任務(wù)。
- TIDYING:所有任務(wù)都已終止,workerCount為0,線程過渡到此狀態(tài)將會調(diào)用
terminate()
方法。 - TERMINATE:
terminate()
方法調(diào)用完成。
為了對runState有序的比較,runState的數(shù)字順序非常重要。runState的狀態(tài)是單調(diào)遞增的,但不一定會到達(dá)每一個(gè)狀態(tài)。它們的轉(zhuǎn)變順序是:
RUNNING -> SHUTDOWN : 在調(diào)用shutdown()方法時(shí),會進(jìn)行這個(gè)轉(zhuǎn)變過程。這個(gè)方法也可能在finalize中調(diào)用。
(RUNNING or SHUTDOWN) -> STOP : 在調(diào)用shutdownNow()時(shí),這一轉(zhuǎn)變發(fā)生。
SHUTDOWN -> TIDYING : 當(dāng)任務(wù)的等待隊(duì)列和任務(wù)池均為空時(shí),進(jìn)行這一轉(zhuǎn)變。
STOP -> TIDYING : 任務(wù)池為空時(shí),發(fā)生這一轉(zhuǎn)變。
TIDYING -> TERMINATED : 當(dāng)terminated()被鉤子方法調(diào)用完成時(shí),進(jìn)行這一轉(zhuǎn)變。
在awaitTermination()中等待的線程會在TERMINATED時(shí)返回。檢測從SHUTDOWN到TIDYING的轉(zhuǎn)變并不簡單,因?yàn)樵赟HUTDOWN狀態(tài)時(shí),任務(wù)隊(duì)列可能在空和非空之間反反復(fù)復(fù)。有時(shí)還需要重復(fù)檢查,這個(gè)會在后面進(jìn)一步說明。
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3;
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// runState is stored in the high-order bits
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
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; }
/*
* Bit field accessors that don't require unpacking ctl.
* These depend on the bit layout and on workerCount being never negative.
*/
private static boolean runStateLessThan(int c, int s) {
return c < s;
}
private static boolean runStateAtLeast(int c, int s) {
return c >= s;
}
private static boolean isRunning(int c) {
return c < SHUTDOWN;
}
這里我們可以看到CAPACITY
其實(shí)是workCount的上限,(1 << (Integer.SIZE - 3)) - 1
即是一個(gè)int型,低29位置1,高位清0。
這樣我們就比較容易理解 workerCountOf
方法的原理,其實(shí)就是取低29位的大小,而runStateOf
就是取高3位的大小。ctlOf
則是兩者之或。
由于ctl是一個(gè)多線程公有的數(shù)據(jù)。所以,對它的修改要格外小心。
在這里是ctl的workCount遞增和遞減的兩個(gè)方法:
/**
* Attempts to CAS-increment the workerCount field of ctl.
*/
private boolean compareAndIncrementWorkerCount(int expect) {
return ctl.compareAndSet(expect, expect + 1);
}
/**
* Attempts to CAS-decrement the workerCount field of ctl.
*/
private boolean compareAndDecrementWorkerCount(int expect) {
return ctl.compareAndSet(expect, expect - 1);
}
這里我們可以看一下AtomicInteger的這個(gè)方法:
/**
* Atomically sets the value to the given updated value
* if the current value {@code ==} the expected value.
*
* @param expect the expected value
* @param update the new value
* @return true if successful. False return indicates that
* the actual value was not equal to the expected value.
*/
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
private void decrementWorkerCount() {
do {} while (! compareAndDecrementWorkerCount(ctl.get()));
}
這個(gè)方法比較簡單,我們可以清晰地看到它會將workCount遞減到0;
workQueue
線程的工作隊(duì)列,是一個(gè)BlockingQueue<Runnable>。這個(gè)成員用于保存任務(wù)和切換任務(wù)。當(dāng)workQueue.poll();
為<b>null</b>時(shí),我們無法保證此時(shí) workQueue
一定為空。所以,我們在SHUTDOWN
或TIDYING
時(shí)只能依賴isEmpty()
方法判斷。這包括了特殊的隊(duì)列,比如DelayQueues,會返回null,然后在delay到期后返回非null的值。
mainLock
mainLock是一個(gè)ReentrantLock
,持有工作線程的設(shè)置和相關(guān)的信息。雖然我們可以使用某種并發(fā)集來完成這個(gè)工作,但事實(shí)證明用鎖會更好。原因之一就是interruptIdleWorkers
系列方法。它們可以避免在shutdown
過程中沒有必要的頻繁中斷。否則,退出線程時(shí)會同步中斷那些還沒有中斷的線程。這也簡化了一些關(guān)聯(lián)的靜態(tài)統(tǒng)計(jì)信息。比如largestPoolSize等等。同樣,我們在shutdown
和shutdownNow
方法中,會持有mainLock
,這樣可以確保線程池在分別退出時(shí)的穩(wěn)定。
workers
workers
是一個(gè)HashSet<Worker>
線程池中所有工作線程的集合。只有當(dāng)mainLock上鎖時(shí),才會被訪問。
termination
awaitTermination的條件鎖,會在shutdown和shutdownNow中調(diào)用。
largestPoolSize
追蹤線程池達(dá)到的最大線程數(shù)。只有在mainLock.lock()后,才能訪問。
completedTaskCount
已完成任務(wù)的數(shù)量。只會在工作線程終止時(shí)更新。同樣,只會在上鎖時(shí)被訪問。
以上就是ThreadPoolExecutor的基本成員。接下來,介紹一下ThreadPoolExecutor的控制變量。由于涉及并發(fā)操作,所有的控制變量都被定義為volatiles
為變量。但是不需要加鎖。因?yàn)樗凶兞慷疾粫袃?nèi)部變量,根據(jù)他們的值,再對它們進(jìn)行改變。
threadFactory
創(chuàng)建新線程的工廠。
/**
* The {@link Executors#defaultThreadFactory} method provides a more
* useful simple implementation, that sets the created thread context
* to known values before returning it.
* @since 1.5
* @author Doug Lea
*/
public interface ThreadFactory {
/**
* Constructs a new {@code Thread}. Implementations may also initialize
* priority, name, daemon status, {@code ThreadGroup}, etc.
*
* @param r a runnable to be executed by new thread instance
* @return constructed thread, or {@code null} if the request to
* create a thread is rejected
*/
Thread newThread(Runnable r);
}
handler
RejectedExecutionHandler類型。在執(zhí)行execute方法時(shí),如果任務(wù)隊(duì)列完全飽和或已經(jīng)關(guān)閉則調(diào)用。
keepAliveTime && allowCoreThreadTimeOut
keepAliveTime控制線程池的超時(shí)機(jī)制。allowCoreThreadTimeOut為true時(shí),任務(wù)如果在等待了keepAliveTime后還沒有被執(zhí)行,任務(wù)就會自行終止。
defaultHandler
線程池默認(rèn)的拒絕策略。
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;
this.thread = getThreadFactory().newThread(this);
}
/** Delegates main run loop to outer runWorker. */
public void run() {
runWorker(this);
}
// Lock methods
//
// The value 0 represents the unlocked state.
// The value 1 represents the locked state.
protected boolean isHeldExclusively() {
return getState() != 0;
}
protected boolean tryAcquire(int unused) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
protected boolean tryRelease(int unused) {
setExclusiveOwnerThread(null);
setState(0);
return true;
}
public void lock() { acquire(1); }
public boolean tryLock() { return tryAcquire(1); }
public void unlock() { release(1); }
public boolean isLocked() { return isHeldExclusively(); }
void interruptIfStarted() {
Thread t;
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
}
Worker
通過繼承AbstractQueuedSynchronizer
實(shí)現(xiàn)了簡單的獨(dú)占鎖。并同時(shí)持有了任務(wù)的Runnable
和Thread
。同時(shí),我們可以看到Worker將大量的工作,委托到外部去做,如將Runnable的.run()委托給runWorker,而自己只管理線程和鎖的狀態(tài)。至于Worker委托的方法,我們順著代碼,在下面講解。
advanceRunState
private void advanceRunState(int targetState) {
for (;;) {
int c = ctl.get();
if (runStateAtLeast(c, targetState) ||
ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
break;
}
}
這個(gè)方法用于提升ctl中state。為了處理并發(fā),在這樣的小方法中,選擇不加鎖,而是使用CAS的方式進(jìn)行同步。每次賦值前先取值,然后通過CAS的原子操作,先進(jìn)行對比,如果值沒有發(fā)生改變,則賦上新的值。如果失敗則不斷地重試。
tryTerminate
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
}
}
這個(gè)方法是線程池嘗試終止的方法。在任何可能讓線程池終止地方都需要調(diào)用(如減少任務(wù)數(shù)量,或是在shutdown時(shí)移除任務(wù))。在這些地方,這個(gè)方法檢測線程池各種狀態(tài),發(fā)現(xiàn)線程池具備了終止的條件,就終止線程池。
首先我們看第一個(gè)判斷,它描述了哪些情況下,不會終止線程池。如果狀態(tài)小于SHUTDOWN 或 如果狀態(tài)不小于TIDYING 或狀態(tài)為SHUTDOWN的同時(shí)工作隊(duì)列不為空。這些情況下,線程池不會終止。
那么滿足線程池終止條件的只有線程池為STOP或是線程池為SHUTDOWN且工作隊(duì)列為空。
如果滿足以上條件,進(jìn)行第二重判斷:如果工作中的任務(wù)不為0,則中止一個(gè)任務(wù)。然后退出。
也就是說,只有在線程池為STOP且沒有工作中的任務(wù)或是線程池為SHUTDOWN且工作隊(duì)列為空。才會真正的terminate。同上面的方法一樣,用CAS進(jìn)行檢測,然后調(diào)用終止。
checkShutdownAccess
private void checkShutdownAccess() {
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkPermission(shutdownPerm);
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers)
security.checkAccess(w.thread);
} finally {
mainLock.unlock();
}
}
}
這個(gè)方法主要做了兩個(gè)工作。第一是檢查調(diào)用者有沒有關(guān)閉線程的權(quán)限。即前面所述的:
private static final RuntimePermission shutdownPerm =
new RuntimePermission("modifyThread");
即是在這里進(jìn)行檢查。
第二個(gè)工作是對每個(gè)Worker進(jìn)行檢查,檢查其調(diào)用者有沒有中斷線程的權(quán)限。
這個(gè)方法會在線程池shutdown
或shutdownNow
時(shí)調(diào)用。
interruptWorkers
private void interruptWorkers() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers)
w.interruptIfStarted();
} finally {
mainLock.unlock();
}
}
對所有Worker
調(diào)用中斷操作。這一段代碼比較簡單,但同樣也埋藏了一些隱患。因?yàn)橛袡?quán)限的影響,可能調(diào)用者無權(quán)中斷部分線程。也就是說,調(diào)用這個(gè)方法后,可能有部分線程依然沒有被中斷。
interruptIdleWorkers
private void interruptIdleWorkers(boolean onlyOne) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
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();
}
}
故名思義,這個(gè)方法就是中斷閑置的線程。我們仔細(xì)對比它與上個(gè)方法的不同,會發(fā)現(xiàn)閑置與否是通過w.tryLock()
判斷出來的。tryLock,我們在前面講Worker的方法時(shí)提到過,它會判斷線程的閑置狀態(tài),不再贅述。
有了上面的代碼作為鋪墊,我們可以開始看execute
方法了。作為我們在使用線程池時(shí)最為常規(guī)的入口,我們從這里開始探索線程池是如何將上面介紹的所有方法,組合起來完成工作的。
execute
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);
}
這里的過程比較復(fù)雜,分為幾種情況:
- 如果當(dāng)前工作的線程數(shù),少于核心線程數(shù)則直接將任務(wù)添加到核心線程。
- 如果能夠加入等待隊(duì)列,又分為兩種情況:
- 如果線程池已經(jīng)關(guān)閉,則直接拒絕任務(wù)。
- 否則如果沒有工程線程就新建一個(gè)非核心線程。
- 如果等待隊(duì)列也無法添加,則直接添加非核心線程。
- 如果第3步失敗,則直接拒絕。
我們可以看到excute中主要調(diào)用的就是addWork
和reject
兩個(gè)方法。reject
方法比較簡單不贅述。我們看一看addWorker
方法。
addWorker
addWorker的方法我們可以分為兩段。第一個(gè)循環(huán)結(jié)束后才會進(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;
// else CAS failed due to workerCount change; retry inner loop
}
}
我們先來看第一個(gè)循環(huán)。這一段是沒有加鎖的,同步方法是通過不斷循環(huán)檢查狀態(tài)。在完成狀態(tài)檢測后進(jìn)入內(nèi)循環(huán)。在內(nèi)循環(huán)中我們可以看到對于線程數(shù)的判斷邏輯。如果compareAndIncrementWorkerCount
返回為true,則workerCount已經(jīng)+1,通過break retry;
退出循環(huán)。如果runStateOf(c) != rs
表示狀態(tài)出現(xiàn)不同步,重新循環(huán)。
這個(gè)循環(huán)完成后,addWorker會進(jìn)入第二段代碼:
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 {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
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;
這一段代碼是加鎖的。先對狀態(tài)進(jìn)行了判斷,然后將新創(chuàng)建的Worker對象加入到worker集合中,最后開始線程。在最后的finally中,對worker添加失敗做了處理調(diào)用了addWorkerFailed
。
private void addWorkerFailed(Worker w) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (w != null)
workers.remove(w);
decrementWorkerCount();
tryTerminate();
} finally {
mainLock.unlock();
}
}
由于我們在添加worker之前就增加了workerCount的值,如果失敗需要-1,保證workerCount的值的正確性。
接下來,我們就需要看看worker的run()
到底是如何運(yùn)行。
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
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())
wt.interrupt();
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 {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
首先通過getTask();
方法獲取任務(wù)。然后還是一樣,檢測線程池狀態(tài),如果線程池處于STOP或更后面的狀態(tài),即可中斷線程。之后便是依次調(diào)用beforeExecute(wt, task)
,task.run()
,afterExecute(task, thrown)
。
我們再看看getTask()方法,看看線程池是如何在池子里找到需要執(zhí)行的任務(wù)的。
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
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 {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
前面的代碼比較簡單,無非取得state
和workercount
。然后檢查一下線程池的狀態(tài),如果已經(jīng)不在工作狀態(tài),則將workercount
減到0,并退出。
接下來就有一句比較重要的代碼,要判斷當(dāng)次取任務(wù),是超時(shí)就作罷,還是一直等待。
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
這里我們可以發(fā)現(xiàn),如果我們在設(shè)置時(shí)設(shè)置了核心線程超時(shí)作罷,或者現(xiàn)在的線程數(shù)量超過核心線程數(shù)時(shí)會啟用超時(shí)作罷。
理一下邏輯就是,超過核心線程數(shù)的線程一定會超時(shí)作罷,但是核心線程則要取決于我們的設(shè)置。
后面我們就可以看到:
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
即是如果擁有超時(shí)機(jī)制就用poll方法,并加上超時(shí)的參數(shù)。如果沒有超時(shí)機(jī)制就直接獲取不會阻塞,如果沒有就是null了。