前言
使用線程池能夠提高線程的復(fù)用率,避免不必要的創(chuàng)建線程,能夠節(jié)約內(nèi)存空間和CPU運(yùn)行時(shí)間。除此之外用線程池作為接口執(zhí)行任務(wù)能夠?qū)⑷蝿?wù)的提交與執(zhí)行解耦,在線程池內(nèi)部決定任務(wù)的分配,具有良好的內(nèi)聚性。
常用線程池
java.util.concurrent.Executors是一個(gè)線程池工廠,負(fù)責(zé)創(chuàng)建常用的線程池,主要有如下幾種:
FixedThreadPool 一個(gè)固定線程數(shù)量的線程池:
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
CachedThreadPool 不固定線程數(shù)量,且支持最大為Integer.MAX_VALUE的線程數(shù)量:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
SingleThreadExecutor 可以理解為線程數(shù)量為1的FixedThreadPool:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
ScheduledThreadPool 支持定時(shí)以指定周期循環(huán)執(zhí)行任務(wù):
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
其中前三種線程池是ThreadPoolExecutor不同配置的實(shí)例,最后一種是ScheduledThreadPoolExecutor的實(shí)例。
ThreadPoolExecutor
先來(lái)通過(guò)ThreadPoolExecutor的構(gòu)造方法了解一下這個(gè)類:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler)
主要參數(shù)有:
corePoolSize 核心線程的數(shù)量,不超過(guò)這個(gè)參數(shù)數(shù)量的線程會(huì)被保留在線程池內(nèi),即使它們是空閑的,如果設(shè)置了allowCoreThreadTimeOut為true除外。
maximumPoolSize 線程池所允許擁有線程的最大數(shù)量,當(dāng)任務(wù)隊(duì)列的任務(wù)已滿,線程數(shù)已達(dá)到最大數(shù)量,任務(wù)會(huì)被拒絕。
keepAliveTime 當(dāng)線程池的線程數(shù)量超過(guò)核心線程的數(shù)量,這些非核心線程會(huì)嘗試在keepAliveTime內(nèi)獲取隊(duì)列內(nèi)的任務(wù),如果獲取失敗則被線程池移除并終止。
unit 超時(shí)時(shí)間的單位。
workQueue 任務(wù)的阻塞隊(duì)列,緩存將要執(zhí)行的Runnable任務(wù),由各線程輪詢?cè)撊蝿?wù)隊(duì)列獲取任務(wù)執(zhí)行。
threadFactory 線程創(chuàng)建的工廠。
handler 當(dāng)任務(wù)由于線程數(shù)量或者任務(wù)隊(duì)列達(dá)到上限,會(huì)執(zhí)行該接口的方法處理任務(wù)的拒絕。
ThreadPoolExecutor的狀態(tài)變量
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; }
其中ctl是ThreadPoolExecutor的同步狀態(tài)變量。
workerCountOf()方法取得當(dāng)前線程池的線程數(shù)量,算法是將ctl的值取低29位。
runStateOf()方法取得線程池的狀態(tài),算法是將ctl的值取高3位:
- RUNNING 111 表示正在運(yùn)行
- SHUTDOWN 000 表示拒絕接收新的任務(wù)
- STOP 001 表示拒絕接收新的任務(wù)并且不再處理任務(wù)隊(duì)列中剩余的任務(wù),并且中斷正在執(zhí)行的任務(wù)。
- TIDYING 010 表示所有線程已停止,準(zhǔn)備執(zhí)行terminated()方法。
- TERMINATED 011 表示已執(zhí)行完terminated()方法。
Executor.execute(Runnable command)
該方法將使用線程池執(zhí)行Runnable對(duì)象的run()方法:
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();
if (workerCountOf(c) < corePoolSize) {
//情況1
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
//情況2
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false)) //情況3
reject(command);
}
以上代碼對(duì)應(yīng)了三種情況:
- 線程池的線程數(shù)量小于核心線程數(shù)量上限,開(kāi)啟核心線程執(zhí)行任務(wù)。
- 線程池的線程數(shù)量不小于核心線程數(shù)量上限,或者開(kāi)啟核心線程失敗,嘗試將任務(wù)以非阻塞的方式添加到任務(wù)隊(duì)列。
- 任務(wù)隊(duì)列以滿導(dǎo)致添加任務(wù)失敗,開(kāi)啟新的非核心線程執(zhí)行任務(wù)。
回顧FixedThreadPool,因?yàn)樗渲玫腸orePoolSize與maximumPoolSize相等,所以不會(huì)執(zhí)行到情況3,并且因?yàn)閣orkQueue為默認(rèn)的LinkedBlockingQueue,其長(zhǎng)度為Integer.MAX_VALUE,幾乎不可能出現(xiàn)任務(wù)無(wú)法被添加到workQueue的情況,所以FixedThreadPool的所有任務(wù)執(zhí)行在核心線程中。
而CachedThreadPool的corePoolSize為0,表示它不會(huì)執(zhí)行到情況1,因?yàn)樗膍aximumPoolSize為Integer.MAX_VALUE,所以幾乎沒(méi)有線程數(shù)量上限,因?yàn)樗膚orkQueue為SynchronousQueue,所以當(dāng)線程池里沒(méi)有閑置的線程SynchronousQueue就會(huì)添加任務(wù)失敗,因此會(huì)執(zhí)行到情況3添加新的線程執(zhí)行任務(wù)。
addWorker方法
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
//第一個(gè)循環(huán)輪詢線程池的狀態(tài),如果線程池處于SHUTDOWN及大于它的狀態(tài)則拒絕執(zhí)行任務(wù)
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;
//第二個(gè)循環(huán)就嘗試將當(dāng)前線程數(shù)+1
//如果是核心線程當(dāng)前線程數(shù)必須小于corePoolSize
//如果是非核心線程則當(dāng)前線程數(shù)必須小于maximumPoolSize
//如果當(dāng)前線程數(shù)小于線程池支持的最大線程數(shù)CAPACITY 也會(huì)返回失敗
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
}
}
//這里已經(jīng)成功執(zhí)行了CAS操作將線程池?cái)?shù)量+1,下面創(chuàng)建線程
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
w = new Worker(firstTask); //firstTask的優(yōu)先級(jí)高于任務(wù)隊(duì)列
final Thread t = w.thread; //Worker內(nèi)部有一個(gè)Thread,并且執(zhí)行Worker的run方法,因?yàn)閃orker實(shí)現(xiàn)了Runnable
if (t != null) {
//這里必須同步在狀態(tài)為運(yùn)行的情況下將Worker添加到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());
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) {
//如果添加成功則運(yùn)行線程
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
addWorker這個(gè)方法先嘗試在線程池運(yùn)行狀態(tài)為RUNNING并且線程數(shù)量未達(dá)上限的情況下通過(guò)CAS操作將線程池?cái)?shù)量+1,接著在ReentrantLock同步鎖的同步保證下判斷線程池為運(yùn)行狀態(tài),然后把Worker添加到HashSet workers中。如果添加成功則執(zhí)行Worker的內(nèi)部線程。
Worker構(gòu)造方法
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
這里指定了第一個(gè)要執(zhí)行的任務(wù),并通過(guò)線程池的線程工廠創(chuàng)建線程。可以發(fā)現(xiàn)這個(gè)線程的參數(shù)為this,即Worker對(duì)象,因?yàn)閃orker實(shí)現(xiàn)了Runnable因此可以被當(dāng)成任務(wù)執(zhí)行,執(zhí)行的即Worker實(shí)現(xiàn)的run方法:
public void run() {
runWorker(this);
}
runWorker方法
因?yàn)閃orker為ThreadPoolExecutor的內(nèi)部類,因此runWorker方法實(shí)際是ThreadPoolExecutor定義的:
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // 因?yàn)閃orker的構(gòu)造函數(shù)中setState(-1)禁止了中斷,這里的unclock用于恢復(fù)中斷
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);
}
}
這個(gè)方法是線程池復(fù)用線程的核心代碼,注意Worker繼承了AbstractQueuedSynchronizer,在執(zhí)行每個(gè)任務(wù)前通過(guò)lock方法加鎖,執(zhí)行完后通過(guò)unlock方法解鎖,這種機(jī)制用來(lái)防止運(yùn)行中的任務(wù)被中斷。在執(zhí)行任務(wù)時(shí)先嘗試獲取firstTask,即構(gòu)造方法傳入的Runnable對(duì)象,然后嘗試從getTask方法中獲取任務(wù)隊(duì)列中的任務(wù)。在任務(wù)執(zhí)行前還要再次判斷線程池是否已經(jīng)處于STOP狀態(tài)或者線程被中斷。
注意這里w.lock方法是在獲取到任務(wù)后才執(zhí)行的,也就是如果線程獲取到任務(wù)前都未加鎖,這樣能保證showDown方法嘗試獲取該鎖中斷空閑的線程,詳見(jiàn)后面的解析。
當(dāng)線程被中斷、拋出異常、不能及時(shí)得到任務(wù),processWorkerExit方法用于最后將線程回收。
getTask方法
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;
}
}
}
還記得Executor.execute方法的情況2是將任務(wù)添加到任務(wù)隊(duì)列,getTask方法就是從任務(wù)隊(duì)列中同步地取出任務(wù)。
這個(gè)方法通過(guò)一個(gè)循環(huán)不斷輪詢?nèi)蝿?wù)隊(duì)列有沒(méi)有任務(wù)到來(lái),首先判斷線程池是否處于正常運(yùn)行狀態(tài),通過(guò)超時(shí)配置有兩種方法取出任務(wù):
- BlockingQueue.poll 阻塞指定的時(shí)間嘗試獲取任務(wù),如果超過(guò)指定的時(shí)間還未獲取到任務(wù)就返回null。
- BlockingQueue.take 這種方法會(huì)在取到任務(wù)前一直阻塞。
FixedThreadPool使用的是take方法,所以會(huì)線程會(huì)一直阻塞等待任務(wù)。CachedThreadPool使用的是poll方法,也就是說(shuō)CachedThreadPool中的線程如果在60秒內(nèi)未獲取到隊(duì)列中的任務(wù)就會(huì)被終止。
到此ThreadPoolExecutor是怎么執(zhí)行Runnable任務(wù)的分析結(jié)束。
ExecutorService.shutdown()
既然有任務(wù)的執(zhí)行,就少不了任務(wù)的終止。ExecutorService是Executor的子類,也是ThreadPoolExecutor的基類。
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(SHUTDOWN);
interruptIdleWorkers();
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate();
}
首先通過(guò)mainLock加鎖同步修改線程池狀態(tài)為SHUTDOWN,然后通過(guò)interruptIdleWorkers方法中斷空閑線程,OnShowDown方法是留給子類去實(shí)現(xiàn)的。
private void interruptIdleWorkers() {
interruptIdleWorkers(false);
}
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()) {
//未中斷的,且tryLock成功
try {
t.interrupt();
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
if (onlyOne) //只做了一次循環(huán)就結(jié)束了,因此最多只能中斷一個(gè)線程
break;
}
} finally {
mainLock.unlock();
}
}
interruptIdleWorkers(boolean onlyOne)方法也是先用mainLock加鎖同步,然后循環(huán)找出所有Worker中Thread未中斷的,通過(guò)tryLock方法嘗試獲取鎖。還記得上文的runWorker方法Worker的鎖是在獲取任務(wù)時(shí)才加的,interruptIdleWorkers方法通過(guò)競(jìng)爭(zhēng)該鎖搶先中斷線程,這樣就導(dǎo)致未執(zhí)行任務(wù)的線程被中斷了,而正在執(zhí)行任務(wù)的線程不受影響,并且可以繼續(xù)執(zhí)行任務(wù)隊(duì)列中的任務(wù)。
ExecutorService.shutdownNow()
與ExecutorService.shutdown()不同的是,shutdownNow方法除了讓線程池拒絕接收新的任務(wù),并且不再執(zhí)行任務(wù)隊(duì)列里未執(zhí)行的任務(wù)。
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;
}
首先mainLock同步將狀態(tài)改為STOP,然后中斷所有線程。
private void interruptWorkers() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers)
w.interruptIfStarted();
} finally {
mainLock.unlock();
}
}
interruptWorkers方法將對(duì)所有Worker執(zhí)行interruptIfStarted,即將所有運(yùn)行中的線程中斷:
void interruptIfStarted() {
Thread t;
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
還記得Worker的構(gòu)造函數(shù)中執(zhí)行了setState(-1),而在runWorker方法中通過(guò)unlock將state改為0,因此可以被interruptWorkers方法中斷。
這里注意的是中斷并不意味著線程就一定停止工作,除非在任務(wù)中捕獲InterruptedException退出任務(wù)。
ExecutorService.submit(Callable<T> task)
使用該方法可以執(zhí)行一個(gè)帶返回值的Callable任務(wù),通過(guò)該對(duì)象的call()方法定義線程要執(zhí)行的事情,同時(shí)call()方法的返回值也由開(kāi)發(fā)者定義,該返回值可以通過(guò)ExecutorService.submit返回的Future對(duì)象的get方法阻塞獲取。
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
submit方法的執(zhí)行過(guò)程中,實(shí)際是通過(guò)newTaskFor方法把Callable對(duì)象轉(zhuǎn)換為RunnableFuture對(duì)象,再由上述的execute(Runnable task)方法執(zhí)行的。
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
newTaskFor方法實(shí)際是構(gòu)造了一個(gè)FutureTask對(duì)象返回,RunnableFuture是FutureTask的超類,并且實(shí)現(xiàn)了Runnable和Future接口。所以execute方法必然會(huì)執(zhí)行FutureTask的run方法。
public interface RunnableFuture<V> extends Runnable, Future<V> {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
*/
void run();
}
public class FutureTask<V> implements RunnableFuture<V> {
...
}
FutureTask工作原理
首先來(lái)看下它的內(nèi)部狀態(tài):
private volatile int state;
private static final int NEW = 0;
private static final int COMPLETING = 1;
private static final int NORMAL = 2;
private static final int EXCEPTIONAL = 3;
private static final int CANCELLED = 4;
private static final int INTERRUPTING = 5;
private static final int INTERRUPTED = 6;
當(dāng)新建一個(gè)FutureTask,其初始狀態(tài)為NEW。
public FutureTask(Callable<V> callable) {
if (callable == null)
throw new NullPointerException();
this.callable = callable;
this.state = NEW; // ensure visibility of callable
}
FutureTask任務(wù)的執(zhí)行
下面分析FutureTask的run方法:
public void run() {
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable<V> c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
result = c.call();
ran = true;
} catch (Throwable ex) {
result = null;
ran = false;
setException(ex);
}
if (ran)
set(result);
}
} finally {
runner = null;
int s = state;
if (s >= INTERRUPTING)
handlePossibleCancellationInterrupt(s);
}
}
在FutureTask的run方法中,可以看到實(shí)際上調(diào)用了內(nèi)部Callable對(duì)象的call方法得到執(zhí)行結(jié)果。而當(dāng)call方法執(zhí)行結(jié)束,如果沒(méi)有異常就執(zhí)行set(result)方法,有異常則執(zhí)行setException方法。
protected void set(V v) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = v;
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
finishCompletion();
}
}
protected void setException(Throwable t) {
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
outcome = t;
UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
finishCompletion();
}
}
兩個(gè)方法都通過(guò)UnSafe類的CAS方法將stateOffSet分別修改為NORMAL或EXCEPTIONAL,然后調(diào)用finishCompletion方法:
private void finishCompletion() {
// assert state > COMPLETING;
for (WaitNode q; (q = waiters) != null;) {
if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
for (;;) {
Thread t = q.thread;
if (t != null) {
q.thread = null;
LockSupport.unpark(t);
}
WaitNode next = q.next;
if (next == null)
break;
q.next = null; // unlink to help gc
q = next;
}
break;
}
}
done();
callable = null; // to reduce footprint
}
該方法遍歷了一個(gè)waiters鏈表,取出WaitNode中封裝的線程,調(diào)用LockSupport.unpark喚醒線程。補(bǔ)充一下LockSupport是JDK中一個(gè)底層同步類,內(nèi)部通過(guò)UnSafe類實(shí)現(xiàn)park與unpark方法用來(lái)阻塞或者喚醒線程。
FutureTask得到任務(wù)的結(jié)果
剩下一個(gè)疑問(wèn),F(xiàn)utureTask調(diào)用get方法是如何阻塞等待結(jié)果的?
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
如果在任務(wù)成功執(zhí)行并返回后調(diào)用get,因?yàn)闋顟B(tài)已經(jīng)更新為NORMAL大于COMPLETING,直接返回report(s),否則執(zhí)行awaitDone方法。
private int awaitDone(boolean timed, long nanos)
throws InterruptedException {
final long deadline = timed ? System.nanoTime() + nanos : 0L;
WaitNode q = null;
boolean queued = false;
for (;;) {
if (Thread.interrupted()) {
removeWaiter(q);
throw new InterruptedException();
}
int s = state;
if (s > COMPLETING) {
if (q != null)
q.thread = null;
return s;
}
else if (s == COMPLETING) // cannot time out yet
Thread.yield();
else if (q == null)
q = new WaitNode();
else if (!queued)
queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
q.next = waiters, q);
else if (timed) {
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
removeWaiter(q);
return state;
}
LockSupport.parkNanos(this, nanos);
}
else
LockSupport.park(this);
}
}
這段代碼需要配合finishCompletion方法理解:
1、當(dāng)任務(wù)已完成,則返回狀態(tài)
2、當(dāng)任務(wù)將要完成通過(guò)Thread.yield()將當(dāng)前線程狀態(tài)從運(yùn)行狀態(tài)變?yōu)榫途w狀態(tài),從而提高其他線程競(jìng)爭(zhēng)運(yùn)行的可能性,將當(dāng)前狀態(tài)改為NORMAL。
3、如果任務(wù)未完成就創(chuàng)建一個(gè)WaitNode對(duì)象,內(nèi)部持有一個(gè)當(dāng)前線程的引用,并且添加到waiters鏈表上,最后通過(guò)LockSupport.park阻塞線程。
因此任務(wù)完成后finishCompletion方法就作用就是將waiters鏈表的每個(gè)WaitNode中的線程喚醒,以便執(zhí)行report(s)。
private V report(int s) throws ExecutionException {
Object x = outcome;
if (s == NORMAL)
return (V)x;
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}
report方法將會(huì)返回給調(diào)用FutureTask.get方法的線程Callable任務(wù)的結(jié)果或者異常。
FutureTask任務(wù)的取消
ExecutorService.submit方法會(huì)返回一個(gè)Future對(duì)象,上文已經(jīng)分析過(guò),它實(shí)際是FutureTask對(duì)象向上轉(zhuǎn)型。
因此Future的cancel方法是在FutureTask中實(shí)現(xiàn)的。
public boolean cancel(boolean mayInterruptIfRunning) {
if (!(state == NEW &&
UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
return false;
try { // in case call to interrupt throws exception
if (mayInterruptIfRunning) {
try {
Thread t = runner;
if (t != null)
t.interrupt();
} finally { // final state
UNSAFE.putOrderedInt(this, stateOffset, INTERRUPTED);
}
}
} finally {
finishCompletion();
}
return true;
}
首先通過(guò)CAS操作嘗試將FutureTask狀態(tài)從NEW改為INTERRUPTING 或CANCELLED。如果FutureTask的狀態(tài)不為NEW,也就是任務(wù)已經(jīng)執(zhí)行完,cancel方法會(huì)被視為無(wú)效返回。因此cancel方法只適用于未開(kāi)始執(zhí)行或者已經(jīng)開(kāi)始執(zhí)行但未完成的任務(wù)。
參數(shù)mayInterruptIfRunning如果為true,則將狀態(tài)設(shè)置為INTERRUPTING,然后嘗試將運(yùn)行任務(wù)的線程runner對(duì)象中斷。回顧run方法中的UNSAFE.compareAndSwapObject(this, runnerOffset,null, Thread.currentThread())就是利用CAS操作將runner對(duì)象賦值為任務(wù)的執(zhí)行線程。最后將FutureTask的狀態(tài)同步設(shè)置為INTERRUPTED。
如果mayInterruptIfRunning為false則不會(huì)嘗試中斷線程,而是將FutureTask的狀態(tài)同步設(shè)置為CANCELLED。
最終執(zhí)行finishCompletion方法,這個(gè)方法上文已經(jīng)分析過(guò),它會(huì)將所有調(diào)用get方法阻塞等待結(jié)果的線程喚醒,并調(diào)用done方法。所以一旦調(diào)用cancel,已經(jīng)調(diào)用get方法的線程將被立即喚醒并返回CancellationException異常。
如果任務(wù)在隊(duì)列里未被執(zhí)行,cancel方法已經(jīng)將FutureTask的狀態(tài)改為INTERRUPTING或者CANCELLED,
當(dāng)線程獲取到這個(gè)FutureTask執(zhí)行它的run方法時(shí),判斷它的狀態(tài)已經(jīng)不是NEW,所以會(huì)直接返回,因此對(duì)于未執(zhí)行的任務(wù)執(zhí)行了cancel方法將不會(huì)被執(zhí)行。
對(duì)于已經(jīng)開(kāi)始執(zhí)行但未執(zhí)行結(jié)束的線程,可以通過(guò)設(shè)置mayInterruptIfRunning為true嘗試將線程中斷從而捕獲異常退出。如果任務(wù)中沒(méi)有處理InterruptedException則會(huì)將任務(wù)執(zhí)行完,但是在執(zhí)行set方法返回結(jié)果時(shí),CAS操作判斷狀態(tài)已經(jīng)不是NEW因此不會(huì)執(zhí)行后續(xù)操作。
ThreadPoolExecutor小結(jié)
分析了ThreadPoolExecutor執(zhí)行execute、submit、shutdown、shutdownNow等方法的執(zhí)行過(guò)程,并且衍生分析了FutureTask的工作原理。發(fā)現(xiàn)其中大量用到了以UnSafe類為基礎(chǔ)的同步鎖,后續(xù)會(huì)繼續(xù)探究ReenTrantLock、AbstractQueuedSynchronizer、LockSupport等同步類的原理。
限于篇幅ScheduledThreadPool 留在以后分析。
最后總結(jié)一下3個(gè)以ThreadPoolExecutor為實(shí)現(xiàn)類的線程池:
- SingleThreadPool 適合維護(hù)固定穩(wěn)定的單線程任務(wù)。
- FixedThreadPool 適合維護(hù)固定且穩(wěn)定的多個(gè)線程,而不用擔(dān)心任務(wù)數(shù)量過(guò)多導(dǎo)致的同時(shí)創(chuàng)建多個(gè)線程的問(wèn)題。它的缺陷是要注意任務(wù)的阻塞問(wèn)題,一旦線程池內(nèi)的所有線程都阻塞或者長(zhǎng)時(shí)間被某個(gè)任務(wù)占用將不會(huì)創(chuàng)建新的線程來(lái)執(zhí)行任務(wù),造成任務(wù)隊(duì)列里的剩余任務(wù)被長(zhǎng)時(shí)間阻塞。
- CachedThreadPool 相比FixedThreadPool更適用于處理要求低延時(shí)的場(chǎng)景,不會(huì)受線程數(shù)量的約束而讓新的任務(wù)等待。但是如果任務(wù)過(guò)多會(huì)導(dǎo)致開(kāi)啟的線程數(shù)量也很多,因此對(duì)內(nèi)存的開(kāi)銷比FixedThreadPool更大,同時(shí)多線程調(diào)度也會(huì)更消耗CPU資源。但是一旦任務(wù)被執(zhí)行完,在等待一段時(shí)間后線程會(huì)被銷毀,因此收縮性較好。
如果以上三種線程不能滿足實(shí)際業(yè)務(wù)的需求,可以自定義參數(shù)創(chuàng)建更符合實(shí)際的ThreadPoolExecutor。