executor属于任务的执行者,这个接口只定义了执行任务的一个方法:
public interface Executor {
void execute(Runnable command);
}
这个从名字我们也可以看到这个接口专门为Executor这个执行器来服务的
里面有定义线程池的关闭,任务是否执行完毕等一些方法,具体就不这里详细说了。
AbstractExecutorService属于对ExecutorService的抽象实现,我们可以通过看ThreadPoolService的源码来了解下这个抽象类
核心的线程池,我们后面专门详细说
jdk给我们提供了生成线程池的这个工具类,我们可以通过Executors来创建适合我们业务场景的线程池
Future以及其实现主要用来获取线程池异步执行的结果
public interface Future {
/**
* 调用该方法,如果任务没有被执行,可以取消该任务
* 如果任务已经开始在执行了,传入的参数来决定是否需要打断正在执行的线程
*/
boolean cancel(boolean mayInterruptIfRunning);
/**
*
*返回是否取消了任务
*/
boolean isCancelled();
/**
*
* @return {@code true} if this task completed
*/
boolean isDone();
/**
*
*同步等待获取任务的执行结果,任务在执行的过程中抛出的异常,会保存下来在这里抛出,这点后面会详细通过一个case讲到
* @throws InterruptedException if the current thread was interrupted
* while waiting
*/
V get() throws InterruptedException, ExecutionException;
/**
*
*同步等待指定的时间
* @throws TimeoutException if the wait timed out
*/
V get(long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
同时继承了Runnable和Future两个接口,即可以获取到异步执行结果Future的Runnable
public interface RunnableFuture<V> extends Runnable, Future<V> {
/**
* Sets this Future to the result of its computation
* unless it has been cancelled.
*/
void run();
}
RunnableFuture的实现,后面也会详细讲到
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,源码中的解释也说到其代表了2个fields:
1. workerCount,线程池中有效的线程个数,即任务数
2. runState,线程池的状态
runState:
1. running:表示线程池处于在运行的状态
2. shutdown:线程池不再接受新的任务了,但是内部队列里的任务会继续执行完
3. stop:不再执行新的任务了,也不接受外部提交的任务,并且会中断所有正在运行的任务
4. tidying:当所有的任务都终止了之后,即ctl==0后,线程池的状态。此时会调用terminated()方法,钩子函数,我们可以重写该函数来自定义
5. terminated:线程池的终止状态
针对这几个状态的关系,我在网上找到了一个图:
我们直接从ThreadPoolExecutor的入口方法来看
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);
}
逻辑分为三步
1. 先获取线程池的状态,如果当前的任务数小于corePoolSize,则创建新的任务来执行
2. 如果当前任务数大于corePoolSize,那就需要把任务放到队列中,如果放入队列成功,需要重新再检查下状态。如果线程池没有正在运行,则删除任务并执行拒绝策略,如果当前的任务数等于0 ,直接启动一个worker,从队列里面获取任务并执行
3. 如果队列也放满了,启动新的worker来执行任务,如果失败执行拒绝策略
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
/**
*状态大于SHUTDOWN,则不能再提交新的任务
*rs == SHUTDOWN 的时候,workQueue 不能为空,并且firstTask必须为空,因为不能再接受新的任务了,除此之外都返回false
*/
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
int wc = workerCountOf(c);
/**
*core参数决定了此时要判断线程池大小该用哪个限制值
*/
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
/**
*一直循环CAS来对线程池的任务数+1,如果成功跳出循环
*/
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来执行任务
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());
//rs < SHUTDOWN 表示处于运行中的线程池, (rs == SHUTDOWN && firstTask == null) 上面解释过,处于SHUTDOWN 中的线程池,不能再接受新的任务了,只能传null
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();
}
//任务添加成功之后,会启动worker线程
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
//任务添加失败会执行失败的逻辑
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
这里我们截取部分worker的核心代码看下
可以看到这里继承了AQS,我们后面会详细的分析到AQS
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
private static final long serialVersionUID = 6138294804551838833L;
Runnable firstTask;
volatile long completedTasks;
Worker(Runnable firstTask) {
setState(-1); // AQS的一个状态器,0表示释放了锁,1表示获取到了锁
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
/** Woker线程的启动直接传入了自身,并调用runWorker方法 */
public void run() {
runWorker(this);
}
}
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // 先释放锁,允许被打断
boolean completedAbruptly = true;
try {
//这里就可以看到,在给上面的addWorker方法传的任务为null的时候,该线程就会不断的从任务队列中获取任务来执行,否则先执行当前提交的任务
while (task != null || (task = getTask()) != null) {
w.lock();
// 如果线程池的状态大于STOP,直接中断
// 如果状态小于Stop,但是线程已经被中断了,再次判断状态是大于或等于Stop,如果是则中断
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
//线程池执行之前调用beforeExecute,执行之后调用afterExecute,这两个方法我们都可以重写,来对实现线程池执行任务的监控
beforeExecute(wt, task);
Throwable thrown = null;
try {
//调用该传递的task的run方法,执行任务
task.run();
//这里重点关注下,遇到异常,会catch并重新抛出,这个我们后面会再次提到
} 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 {
//任务执行完成之后,该线程的任务数加1,并解锁
task = null;
w.completedTasks++;
w.unlock();
}
}
//如果该线程是正常执行结束,则completedAbruptly 变量为false
completedAbruptly = false;
} finally {
//执行完任务之后处理线程是否要退出的逻辑
processWorkerExit(w, completedAbruptly);
}
}
当worker线程执行完一个任务后,会通过getTask()方法不断的从队列里面去任务来执行,我们看下这个方法的逻辑
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// 线程只要大于或等于SHUTDOWN ,那处于STOP及以上的状态,线程需要退出,或者队列为空了,线程也需要退出
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
//如果当前任务数大于最大任务数限制,那当前线程需要退出
//如果当前任务数大于核心线程数或者允许核心线程超时退出,这个时候只要线程数大于0或者队列为空,就需要退出该线程
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;
}
}
}
任务执行完成之后,需要处理线程退出的逻辑,即processWorkerExit
private void processWorkerExit(Worker w, boolean completedAbruptly) {
// 如果线程是正常退出了循环体,那在getTask这个方法里已经调整了workerCount,如果是异常退出了循环体,那说明之前没有调整过workerCount,所以这里需要调整下
if (completedAbruptly)
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();//这里加锁和addWorker方法加锁的逻辑是一样的,因为要处理workers,而workers不是线程安全的,是一个HashSet
try {
completedTaskCount += w.completedTasks;
workers.remove(w);
} finally {
mainLock.unlock();
}
//每个线程退出之前都尝试终止线程池
tryTerminate();
int c = ctl.get();
//如果线程池的状态 >= STOP 线程如果是正常退出的,最小线程数如果是0并且队列不为空,修改最小线程数为1,当前线程数如果小于最小线程数,需要补充线程数。
//反之,,如果线程是异常退出的,需要补充一个新的线程来处理
if (runStateLessThan(c, STOP)) {
if (!completedAbruptly) {
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
if (min == 0 && ! workQueue.isEmpty())
min = 1;
if (workerCountOf(c) >= min)
return; // replacement not needed
}
addWorker(null, false);
}
}
到这里我们已经分析了添加任务,执行任务,以及执行结束退出任务的逻辑,接下来我们再看下添加任务失败之后的处理逻辑:addWorkerFailed
private void addWorkerFailed(Worker w) {
final ReentrantLock mainLock = this.mainLock;
//添加任务失败之后,获取当前的主锁,删除当前的worker,并调整workerCount,再尝试终止线程池
mainLock.lock();
try {
if (w != null)
workers.remove(w);
decrementWorkerCount();
tryTerminate();
} finally {
mainLock.unlock();
}
}
在处理任务执行结束退出的时候和添加任务失败的时候,都会调用
tryTerminate();
来尝试终止线程池
final void tryTerminate() {
for (;;) {
int c = ctl.get();
//如果线程池处于运行态,不处理
//如果线程池已经终止了,不处理
//如果线程池是SHUTDOWN 且队列不为空,也不能处理,因为这个时候还需要保证队列的任务处理完毕
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 {
//如果状态是TIDYING 调用钩子方法,我们可以实现terminated()来处理我们自定义的一些逻辑
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
}
}
private void interruptIdleWorkers(boolean onlyOne) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers) {
Thread t = w.thread;
//只要能获取到worker的锁,那说明worker处于空闲状态,这个时候如果worker没有被打断,则打断worker
if (!t.isInterrupted() && w.tryLock()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
if (onlyOne)
break;
}
} finally {
mainLock.unlock();
}
}
至此,我们已经分析了整个线程池从提交任务到执行任务结束的过程,我们简单总结下这个过程:
1. 通过addWorker添加任务,启动一个Worker线程来执行新提交的任务
2. Worker线程的run方法直接调用了ThreadPoolExecutor的runWorker方法
3. 在runWorker里,首先执行刚刚提交的任务,如果这个任务执行完了会继续通过getTask方法不断的从队列里面获取新的任务继续执行,如果getTask返回null表示该线程需要退出了,需要注意的是此时在getTask里面已经提交调整了workerCount,这表示线程正常执行结束了,不需要在再次调整workerCount了
4. 线程执行结束之后会调用processWorkerExit来处理线程退出的逻辑,对应上一步的getTask,如果线程属于异常退出,那说明没有来得及调整workerCount,所以需要调整下workerCount,然后再进行删除任务,调整已经完成的任务数,并调用tryTerminate()方法尝试终止线程池
5. 如果第一步的addWorker方法添加任务失败之后,会调用addWorkerFailed()方法,删除该任务,调整workerCount,并调用tryTerminate()方法尝试终止线程池
6. 在tryTerminate()方法里会尝试中断所有处于空闲状态的线程,并回调钩子方法terminated(),并修改线程池的状态为终止状态。到此线程池终止了。
我们可以看到在线程池整个运行的周期内,每个线程执行结束之后都会尝试终止线程池,这个是线程池会自动终止自身,那我们也可以直接通过线程池的方法来终止整个线程池,这里我们常用的有两个方法,shutDown()和shutDownNow()
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//shutDown的访问检查,通过java的安全管理器,首先会获取安全管理器,如果有的话,确保当前的调用者有权限来shut down线程
//如果有,需要进一步判断调用者有打断每一个线程的权限
checkShutdownAccess();
//修改状态为SHUTDOWN
advanceRunState(SHUTDOWN);
//打断所有的空闲线程
interruptIdleWorkers();
//钩子回调方法
onShutdown();
} finally {
mainLock.unlock();
}
tryTerminate();
}
public List shutdownNow() {
List tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//安全检查
checkShutdownAccess();
//修改状态为STOP
advanceRunState(STOP);
//注意这里是要打断所有的线程,并非只是空闲的线程
interruptWorkers();
//返回队列中还没有执行的任务
tasks = drainQueue();
} finally {
mainLock.unlock();
}
//尝试再终止线程池,在shutDown中没有调用这个方法的原因是队列里面可能还有任务,必须得确保队列中的任务执行结束之后才能终止线程池
tryTerminate();
return tasks;
}
private void interruptWorkers() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers)
w.interruptIfStarted();
} finally {
mainLock.unlock();
}
}
void interruptIfStarted() {
Thread t;
//worker中只有state大于或者等于0表示线程已经被启动了,可以去中断,否则没有中断的必要
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
至此我们分析了整个线程池从提交任务到任务的执行,以及线程池的终止部分的逻辑,而这里我们一直看的都是线程池执行的没有任何返回值的任务,我们知道,在ExecutorService接口中有个submit()方法,专门来执行有返回结果的任务,这个方法的是实现在AbstractExecutorService中,那我们从这个入口再来看下线程池执行任务的逻辑,后面我们再看submit和execute两个在使用的过程中有什么需要注意的。
public Future submit(Callable task) {
if (task == null) throw new NullPointerException();
RunnableFuture ftask = newTaskFor(task);
//可以看到这里还是复用了子类ThreadPoolExecutor的execute方法,只不过我们之前看到的execute传递的是Runnable
//但是现在变成了RunnableFuture这个类,那说明真正的执行逻辑需要看下newTaskFor这个方法返回的RunnableFuture的执行逻辑
execute(ftask);
return ftask;
}
protected RunnableFuture newTaskFor(Runnable runnable, T value) {
return new FutureTask(runnable, value);
}
我们主要就看下FutureTask的run方法,在看这个run方法之前我们先看下FutureTask的状态:
/**
* The run state of this task, initially NEW. The run state
* transitions to a terminal state only in methods set,
* setException, and cancel.
* Possible state transitions:
* NEW -> COMPLETING -> NORMAL
* NEW -> COMPLETING -> EXCEPTIONAL
* NEW -> CANCELLED
* NEW -> INTERRUPTING -> INTERRUPTED
*/
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;
初始化的时候,状态即为new:
public FutureTask(Runnable runnable, V result) {
this.callable = Executors.callable(runnable, result);
this.state = NEW; // ensure visibility of callable
}
public void run() {
//线程的执行要从开始来执行,如果状态已经不是new了,那说明已经在执行中了,直接返回
//如果不能把执行该任务的runner改为当前线程,那当前线程也就无法执行该任务了
if (state != NEW ||
!UNSAFE.compareAndSwapObject(this, runnerOffset,
null, Thread.currentThread()))
return;
try {
Callable c = callable;
if (c != null && state == NEW) {
V result;
boolean ran;
try {
//我们之前也分析到,这个任务的执行会在Worker中执行,而真正的执行是直接调用了run方法,可以看下上文runWorker的源码分析
//那这里我们需要重点看下,run方法也是会执行callable的call方法,而重点是此时如果出现异常,不会像runWorker中抛出该异常,而是catch住了
//临时保存下来,那说明在这个方法中其实异常已经被吞了,回过头再看runWorker那个方法,在调用了FutureTask的run之后不会抛出任何异常
//那也就是说我们要感知这个异常,得通过其他方法来获取,即我们要分析的get方法
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);
}
}
我们分别看下在任务执行的过程中,如果遇到异常以及正常结束后续的逻辑是怎样的
protected void setException(Throwable t) {
//先把状态改为COMPLETING ,然后保存异常,再把状态改为异常的终态,即满足状态变化中的这种: NEW -> COMPLETING -> EXCEPTIONAL
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
//保存异常
outcome = t;
//终态
UNSAFE.putOrderedInt(this, stateOffset, EXCEPTIONAL); // final state
//处理任务结束的逻辑
finishCompletion();
}
}
protected void set(V v) {
//我们可以看到跟异常结束的类似,满足的状态变化为:NEW -> COMPLETING -> NORMAL
if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
//保存了执行的结果
outcome = v;
//终态
UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
//处理任务结束的逻辑
finishCompletion();
}
}
在任务不论是正常结束还是异常结束,最后都会通过finishCompletion()方法来处理完成任务的逻辑
private void finishCompletion() {
//waiters:当前任务还没有结束的时候,调用该任务的get方法之后会把这些线程放到等待队列waiters中,任务结束之后需要把这些线程全部都唤醒
for (WaitNode q; (q = waiters) != null;) {
//首先CAS把当前线程的waiters改为Null,这里都调用了UNSAFE这个底层类,通过CAS来操作,我们会在后续的篇幅中专门讲这个
if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) {
for (;;) {
Thread t = q.thread;
//挨个把waiters中的所有WaitNode中的线程全部唤醒,这里是通过LockSupport这个类来完成,同样我们后续会提到这个类
if (t != null) {
//等待节点对应的线程指向空
q.thread = null;
//唤醒等待的线程
LockSupport.unpark(t);
}
//这里稍微说下,我们在处理链表中的节点,当前节点处理完成之后,一定要令其引用指向Null
//这样也方便gc的时候能把这些处理掉,我们可以简单看下这个处理步骤:保存当前节点的next,并把当前节点的next指向Null,然后再把当前节点的索引指向当前节点的next
WaitNode next = q.next;
if (next == null)
break;
q.next = null; // unlink to help gc
q = next;
}
break;
}
}
//钩子方法,我们可以自定义来实现任务结束后的一些逻辑
done();
callable = null; // to reduce footprint
}
那在处理完任务的执行之后,也唤醒了等待队列里的所有的线程之后,那些线程就可以获取结果了,下面我们看下结果的获取。
public V get() throws InterruptedException, ExecutionException {
int s = state;
//任务的状态没有完成就会一直在这里等待
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
private int awaitDone(boolean timed, long nanos)
throws InterruptedException {
//需要指定等待时间的话,等待的时间为当前时间加上等待的时间,否则为0
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) {
//如果任务已经结束,当前等待线程对应的节点如果不为空,把该节点中指向的线程指向null,返回状态
if (q != null)
q.thread = null;
return s;
}
//任务还没有结束并且处于COMPLETING,让出cpu的执行权
else if (s == COMPLETING) // cannot time out yet
Thread.yield();
else if (q == null)
//如果等待节点为空,新创建一个等待节点,也就是把当前线程包装成一个waitNode,并把waitNode的thread属性指向当前线程
q = new WaitNode();
else if (!queued)
//当前节点创建成功之后还没加入到waiters这个等待链表中,所以先加入
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);
}
}
我们再看下awaitDone()方法中的几个辅助方法
WaitNode节点的定义:
static final class WaitNode {
//保存当前线程
volatile Thread thread;
//链表中指向下一个节点
volatile WaitNode next;
WaitNode() { thread = Thread.currentThread(); }
}
private void removeWaiter(WaitNode node) {
if (node != null) {
//把node节点的线程指向空,那下面循环删除的时候,就只把waiters中处于线程为空的节点全部删除
node.thread = null;
retry:
for (;;) {
//这里简单说下这个删除的逻辑,首先q指向waiters,pred即q的前一个节点,s为q的后一个节点
for (WaitNode pred = null, q = waiters, s; q != null; q = s) {
s = q.next;
//如果q对应的thread一开始就为Null,那直接跳转到第三个if条件中,修改当前FutureTask中的waiters的指向为s,即删除了q这个节点
//否则pred指向q,继续下一次循环
if (q.thread != null)
pred = q;
//如果q对应的线程为Null,而此时的pred不为null,那就把q的前一个节点的next指向s,即删除了q节点,如果此时pred对应的thread也为null,那重新开始retry
else if (pred != null) {
pred.next = s;
if (pred.thread == null) // check for race
continue retry;
}
else if (!UNSAFE.compareAndSwapObject(this, waitersOffset,
q, s))
continue retry;
}
break;
}
}
}
从get方法我们看到,awaitDone方法返回之后,就要向外通知当前任务的最新的状态:
private V report(int s) throws ExecutionException {
Object x = outcome;
//这个时候我们看到了之前执行任务的时候正常结束和异常结束在outcome里保存的值有用了
//如果正常结束,直接返回对应的值即为当前任务执行结束返回的结果
//如果是被取消了或者中断,抛出取消的异常
//剩下的就是异常结束,直接抛出这个异常,那从这里我们也可以看到,当前任务执行的过程中的异常时通过在get方法里面的report方法向外抛出的,也就是我们如果不调用get这个方法,是无法知道我们提交的任务是否出现异常,这个也就是对应了如果我们用execute来当线程池里提交Callable,那异常是会被吞掉的,这个我们后面会有case来专门说明这点
if (s == NORMAL)
return (V)x;
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}
至此我们也已经分析了线程池的获取异步执行结果的逻辑源码,我们简单总结下这部分
1. 通过submit来往线程池提交Callable任务,在submit中会把当前的任务包装成一个FutureTask,我们前面在线程池的基础架构中有讲到这个,接下来会把这个任务通过execute提交到线程池,那从我们分析Worker知道核心的执行逻辑是FutureTask的run方法
2. 在run方法中,会调用当前提交的任务的call方法获取执行的结果,出现异常返回或者正常返回结果,都会分别修改state,并且保存执行的结果到outcome中
3. 任务执行结束之后,设置完了state的状态,会调用finishCompletion方法,把处于等待链表waiters中的所有节点对应的线程全部唤醒,并且删除等待节点
4. 所有的线程在通过get方法获取结果的时候都会同步等待,等待结果执行完毕,通过awaitDone方法
5. 在waitDone方法中会把每一个处于等待的线程封装成一个WaitNode节点,并加入到waiters这个链表中,如果当前等待的线程被打断会删除该等待节点,并抛出中断异常,否则即阻塞当前线程,这个就会和之前的finishCompletion()方法来唤醒等待线程相呼应
6. awaitDone方法返回当前任务的状态,然后会通过report()方法来发布该状态,并且返回执行的结果或者抛出异常。
至此我们已经完整的分析了整个线程池的源码部分,那我们分析了这么多,主要的作用还是我们通过分析到的这些逻辑在实际的使用中来避免遇到一些坑,这个其实在分析源码逻辑的时候,有部分的提到,由于本次篇幅太长,我分为2篇,在下一篇我们来看下在使用线程池的过程中我们需要避免哪些坑。
参考
http://ifeve.com/java-threadpool/ http://blog.163.com/among_1985/blog/static/275005232012618849266/
http://developer.51cto.com/art/201203/321885.htm
http://blog.csdn.net/java2000_wl/article/details/22097059
http://blog.csdn.net/cutesource/article/details/6061229
http://blog.csdn.net/xieyuooo/article/details/8718741
http://cmsblogs.com/?p=2448