阅读须知
- JDK版本:1.8
- 文章中使用/* */注释的方法会做深入分析
正文
构造方法:
ThreadPoolExecutor:
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.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
构造方法参数解释:
- corePoolSize:向线程池中提交任务时,线程池会创建一个新线程来执行任务,直到当前线程数等于 corePoolSize,这时后面再次提交的任务会被保存到阻塞队列中,等待被执行。如果执行了线程池的 prestartAllCoreThreads 方法,线程池会提前创建并启动所有核心线程
- maximumPoolSize:线程池线程数量的最大上限,如果当前阻塞队列满了,且继续提交任务,则创建新的线程执行任务,前提是当前线程数小于 maximumPoolSize
- keepAliveTime:线程空闲时的存活时间,即当线程没有任务执行时,继续存活的时间,默认情况下,该参数只在线程数大于 corePoolSize 时才有用
- unit:是 keepAliveTime 的时间单位
- workQueue:任务存储的队列,用来保存等待被执行的任务的阻塞队列,在 JDK 中提供了如下阻塞队列:
- ArrayBlockingQueue:基于数组结构的有界阻塞队列,按 FIFO 排序任务
- LinkedBlockingQueue:基于链表结构的阻塞队列,按 FIFO 排序任务,吞吐量通常要高于ArrayBlockingQueue
- SynchronousQueue:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于 LinkedBlockingQueue
- priorityBlockingQueue:具有优先级的无界阻塞队列
- threadFactory:创建线程的工厂,默认为 Executors.DefaultThreadFactory,里面定义了线程计数,并且给生产的线程命名(pool-线程池号-thread-线程号),可以自定义,实现 ThreadFactory 重写 newThread 方法
- handler:饱和策略,可以自定义,实现 RejectedExecutionHandler 重写 rejectedExecution 方法
- AbortPolicy:默认饱和策略,直接抛出一个 RejectedExecutionException 异常,让调用者自己处理
- DiscardPolicy:后续的任务都抛弃掉
- DiscardOldestPolicy:会将等待队列里最旧的任务踢走,让新任务得以执行
- CallerRunsPolicy:它既不抛弃新任务,也不抛弃旧任务,而是直接在当前线程运行这个任务,当前线程一般就是主线程,主线程运行任务,有可能会阻塞,推荐少用
向线程池提交任务时会用到 execute 方法:
ThreadPoolExecutor:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
/* 如果当前线程数量小于 corePoolSize,添加 worker 执行任务 */
if (addWorker(command, true))
return;
c = ctl.get();
}
// 判断是否在 RUNNING 状态并且可以成功将任务添加到队列中
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
// 二次检查运行状态,如果不是 RUNNING 并且可以成功从队列中移除,则拒绝任务走饱和策略
reject(command);
else if (workerCountOf(recheck) == 0)
// 如果线程池中已经没有线程,则添加一个线程
addWorker(null, false);
}
// 如果添加队列失败,尝试增加 worker 来执行任务
else if (!addWorker(command, false))
// 如果上面失败,则说明线程已经超过 maximumPoolSize 或者已关闭,则拒绝任务走饱和策略
reject(command);
}
这里翻译一下方法中给出的注释:
分三步进行:
- 如果少于 corePoolSize 个线程正在运行,尝试使用给定command作为第一个任务启动一个新线程,调用 addWorker 方法时以原子方式检查 runState 和 workerCount ,返回 false 防止在不应该增加时增加线程。
- 如果任务可以成功入队,仍然需要二次检查是否应该添加一个线程(因为线程池在第一次检查之后可能已经死掉了)或者自进入此方法后线程池关闭了。所以需要重新检查状态,如果有必要的话回滚队列(将本次新提交的任务从队列中移除),否则就添加一个新的线程。
- 如果任务不能入队,则尝试添加一个新线程。如果添加新线程失败,则证明线程池已关闭或已饱和,因此拒绝任务。
下图可以很清晰的表示这个过程:
方法里获取线程池的运行状态和线程数量都和一个叫做 ctl 的变量有关,这里翻译一下变量的注释:
主要的线程池控制状态,ctl 是一个包含两个概念字段的原子整数
- workerCount:表明线程的有效数量
- runState:表明是否运行,关闭等
为了将它们打包到一个 int 中,我们将 workerCount 限制为(2 ^ 29)-1(约5亿)个线程,而不是(2 ^ 31)-1(20亿),如果未来这变成了一个问题,则可以将变量可以改为 AtomicLong ,并调整下面的 shift / mask 常量。但在问题出现之前,使用 int 可以更快更简单。
workerCount 是已经被允许启动并且不被允许停止的 worker 的数量。该值可能与活动线程的实际数量暂时不同,例如,当 ThreadFactory 在被询问时未能创建线程,以及退出线程在终止之前仍在执行时。用户可见池大小作为 worker 组的当前大小。
runState 提供了主要的生命周期控制,取值为:- RUNNING:接受新任务并处理排队的任务
- SHUTDOWN:不接受新任务,但处理排队的任务
- STOP:不接受新任务,不处理排队的任务,并中断正在进行的任务
- TIDYING:所有任务都已终止,workerCount 为零,转换为状态 TIDYING 的线程将运行 terminate 钩子方法
- TERMINATED:terminate 已完成
这些值之间的数字顺序很重要,可以进行有序的比较。runState 随着时间的推移单调增加,但不需要过每个状态。状态转换过程为:- RUNNING -> SHUTDOWN:在调用 shutdown 时,可能会隐含在 finalize 中
- (RUNNING or SHUTDOWN) -> STOP:在调用 shutdownNow 时
- SHUTDOWN -> TIDYING:当队列和池都是空的时候
- STOP -> TIDYING:当池是空的时候
- TIDYING -> TERMINATED:当 terminate 钩子方法完成时
当状态达到 TERMINATED 时,在 awaitTermination 中等待的线程将返回。
检测从 SHUTDOWN 到 TIDYING 的转换并不像你想的那样直接,因为在 SHUTDOWN 状态期间非空队列可能变空,反之亦然,但是我们只能在看到它为空时看到 workerCount 是0(有时需要重新检查 - 见下文)。
这个注释对理解线程池的工作流程非常重要,这里看到 ctl 表示了两个变量,其中低29位表示线程池中线程数 workerCount,通过高3位表示线程池的运行状态 runState,代码中相关变量的声明和计算如下:
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// 29
private static final int COUNT_BITS = Integer.SIZE - 3;
// runState 存储在高位中
// 线程容量536870911
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// 高三位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;
// 打包和解包 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; }
接下来是添加线程执行任务的过程:
ThreadPoolExecutor:
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
// 获取运行状态
int rs = runStateOf(c);
// 判断如果运行状态大于等于 SHUTDOWN,并且(在状态等于 SHUTDOWN 任务为 null 时检查队列是否为空),满足上述条件,则直接返回,不处理任何任务
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
// 获取线程池当期的线程数量
int wc = workerCountOf(c);
// 判断当前线程数量如果大于等于线程容量或者根据传入的 core 布尔值判断当前线程数量是否大于等于 corePoolSize 或 maximumPoolSize,满足上述条件,则直接返回,不处理任何任务
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
// 尝试 CAS 递增 ctl 的 workerCount 字段
if (compareAndIncrementWorkerCount(c))
break retry;
// 重新读取 ctl
c = ctl.get();
if (runStateOf(c) != rs)
// 如果运行状态改变了,重新进入这个循环
continue retry;
// 否则由于 workerCount 更改 CAS 失败; 重试内部循环
}
}
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
/* 创建 worker */
w = new Worker(firstTask);
// 获取 worker 的线程
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
int rs = runStateOf(ctl.get());
// 判断运行状态是否小于 SHUTDOWN 也就是 RUNNING 状态或者运行状态为 SHUTDOWN 并且提交的任务为 null
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
// 预先检查线程是否可以启动
if (t.isAlive())
throw new IllegalThreadStateException();
// 将 Worker 添加到线程池集合中,集合包含池中的所有工作线程,只有在持有 mainLock 时才能访问
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
// 设置此时线程池中的最大线程数量
largestPoolSize = s;
// 设置成功添加 worker 标记
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
// 如果成功添加 worker 则启动线程
t.start();
// 设置 worker 启动成功标记
workerStarted = true;
}
}
} finally {
if (! workerStarted)
/* 添加 worker 失败处理 */
addWorkerFailed(w);
}
return workerStarted;
}
Worker 继承了 AQS(AbstractQueuedSynchronizer)并且实现了 Runnable:
ThreadPoolExecutor.Worker:
Worker(Runnable firstTask) {
// 设置 AQS 状态,禁止中断,直到 runWorker
setState(-1);
this.firstTask = firstTask;
// 线程工厂创建线程
this.thread = getThreadFactory().newThread(this);
}
线程工厂默认为 Executors.DefaultThreadFactory,这里创建线程时传入了 this,也就是 Worker,Worker 实现了 Runnable,所以线程启动之后就会运行它的 run 方法:
ThreadPoolExecutor.Worker:
public void run() {
/* 委托外部 runWorker 方法运行 */
runWorker(this);
}
ThreadPoolExecutor:
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
// 允许中断
w.unlock();
// 标记线程是否突然终止
boolean completedAbruptly = true;
try {
/* 如果当前任务不为 null,则运行当前任务,否则尝试获取任务执行 */
while (task != null || (task = getTask()) != null) {
w.lock();
// 如果线程池已停止,确保线程中断; 如果没有,确保线程不中断。这需要在第二种情况下进行重新检查,以便在清除中断时处理 shutdownNow 竞争
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;
// 增加 worker 完成的任务数量
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
/* worker 退出处理 */
processWorkerExit(w, completedAbruptly);
}
}
ThreadPoolExecutor:
private Runnable getTask() {
// 标记最后一次 poll 是否超时了
boolean timedOut = false;
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// 判断是否运行状态大于等于 SHUTDOWN 并且运行状态大于等于 STOP 或者任务队列为空
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
// 循环 CAS 递减 ctl 的 workerCount 字段,直到成功
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// 标记是否允许杀死 worker(判断是否允许核心线程超时,默认为 false 或者当前线程数量大于 corePoolSize)
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
// CAS 递减 ctl 的 workerCount 字段
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
// 从队列中获取任务
Runnable r = timed ?
// 如果在 keepAliveTime 时间内,阻塞队列还是没有任务,则返回 null
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
// 如果阻塞队列为空,当前线程会被挂起等待;当队列中有任务加入时,线程被唤醒, take 方法返回任务,并执行
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
getTask 就是循环不停的从队列中获取任务,在文章开始介绍的 keepAliveTime 参数,就是在这里生效的,这里说明一下这个过程:
- 当 timed 为 true 时,也就是允许核心线程超时或者当前线程数量大于 corePoolSize 时,这时会调用
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)
进行任务的获取,如果在 keepAliveTime 时间内,阻塞队列还是没有任务,则返回null - 返回 null 会将 timedOut 标记为 true,表示 poll 操作超时了
- 进入下一次循环,判断(当前线程数大于 maximumPoolSize 或者(timed 为 true 并且 timedOut 为 ture))并且(当前线程数大于1或者任务队列为空),这时会 CAS 递减 ctl 的 workerCount 字段,也就是减少线程的数量,并返回 null
- getTask 方法返回 null 后调用栈上层的 runWorker 方法的 while 条件就无法满足,这时会进入 processWorkerExit 方法进行 worker 退出处理,processWorkerExit 方法会移除 worker,来看这个方法
ThreadPoolExecutor:
private void processWorkerExit(Worker w, boolean completedAbruptly) {
// 如果突然终止,那么 workerCount 不会被调整
if (completedAbruptly)
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 完成的任务数量累加当前 worker 完成的任务数量
completedTaskCount += w.completedTasks;
// 移除当前 worker
workers.remove(w);
} finally {
mainLock.unlock();
}
/* 尝试终止 */
tryTerminate();
int c = ctl.get();
if (runStateLessThan(c, STOP)) {
// 判断线程是否没有突然终止
if (!completedAbruptly) {
// 确定最小线程数量
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
if (min == 0 && ! workQueue.isEmpty())
min = 1;
// 如果当前线程数量大于等于最小线程数量,直接返回
if (workerCountOf(c) >= min)
return;
}
// 增加一个线程
addWorker(null, false);
}
}
ThreadPoolExecutor:
final void tryTerminate() {
for (;;) {
int c = ctl.get();
// 如果运行状态为 RUNNING 状态或者大于等于 TIDYING 或者(运行状态为 SHUTDOWN 状态并且队列不为空,直接返回)
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return;
if (workerCountOf(c) != 0) {
// 中断一个闲置的线程
interruptIdleWorkers(ONLY_ONE);
return;
}
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
// Executor 终止时调用的方法,默认实现什么都不做,留给子类实现,注意:要正确嵌套多个重写,子类通常应该在此方法中调用 super.terminated
terminated();
} finally {
ctl.set(ctlOf(TERMINATED, 0));
// 唤醒所有等待的线程
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
// 否则在失败的 CAS 上重试
}
}
整个 addWorker 方法添加线程执行任务的流程到这里就完成了,接下来是添加 worker 失败的处理:
ThreadPoolExecutor:
private void addWorkerFailed(Worker w) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (w != null)
// 移除当前 worker
workers.remove(w);
// 循环 CAS 递减 ctl 的 workerCount 字段,直到成功
decrementWorkerCount();
// 尝试终止,上文已经分析过这个方法
tryTerminate();
} finally {
mainLock.unlock();
}
}
到这里,整个 execute 方法执行过程的分析就完成了。为线程池提交任务还可以使用 submit 方法,使用 submit 提交的任务可以获取任务执行的返回值:
AbstractExecutorService:
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
// 将任务封装到 FutureTask 中
RunnableFuture<T> ftask = newTaskFor(task);
// RunnableFuture 接口继承了 Runnable 接口,所以可以使用 execute 方法来执行
execute(ftask);
// 返回执行结果
return ftask;
}
最后是关闭线程池的相关实现,可以使用 shutdown 和 shutdownNow 两个方法来关闭线程池,区别是 shutdown 方法将执行平缓的关闭过程,不再接受新的任务,同时等待已经提交的任务执行完成,包括那些还未开始执行的任务。shutdownNow 方法将执行粗暴的关闭过程,它将尝试取消所有运行中的任务,并且不再启动队列中尚未开始执行的任务。
ThreadPoolExecutor:
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 检查调用者是否有权限中断 worker 线程
checkShutdownAccess();
// 将运行状态过渡到 SHUTDOWN,如果运行状态大于等于 SHUTDOWN,则保持不变
advanceRunState(SHUTDOWN);
// 中断所有闲置的线程,上文已经分析过
interruptIdleWorkers();
// shutdown 钩子方法,默认空实现,由子类覆盖,ScheduledThreadPoolExecutor 对其进行了覆盖来取消延迟任务
onShutdown();
} finally {
mainLock.unlock();
}
// 尝试终止,上文已经分析过这个方法
tryTerminate();
}
ThreadPoolExecutor:
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 检查调用者是否有权限中断 worker 线程
checkShutdownAccess();
// 将运行状态过渡到 STOP,如果运行状态大于等于 STOP,则保持不变
advanceRunState(STOP);
// 中断所有线程,即使处于活动状态,忽略 SecurityExceptions(在这种情况下,某些线程可能保持不中断)
interruptWorkers();
/* 移除队列中的任务,并返回这些任务 */
tasks = drainQueue();
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
ThreadPoolExecutor:
private List<Runnable> drainQueue() {
BlockingQueue<Runnable> q = workQueue;
ArrayList<Runnable> taskList = new ArrayList<Runnable>();
// BlockingQueue 的 drainTo 方法会移除此队列中所有可用的元素,并将它们添加到给定 collection 中
q.drainTo(taskList);
// 如果队列是 DelayQueue 或其他类型的队列,poll 或 drainTo 可能无法删除某些元素,则会逐个删除它们
if (!q.isEmpty()) {
for (Runnable r : q.toArray(new Runnable[0])) {
if (q.remove(r))
taskList.add(r);
}
}
return taskList;
}
到这里,整个线程池 ThreadPoolExecutor 的源码解析就完成了。