ThreadPoolExecutor源码解读
ThreadPoolExecutor的基本认识
类的继承图
从Executor看起:
只定义了一个execute的方法,再看看ExecutorService:
这里定义了修改线程池状态、查看线程池状态、任务提交、任务执行的方法。
在AbstractExecutorService中,对submit和invoke的方法进行了模板实现,实现方式后面具体再分析。
再来看下ThreadPoolExecutor自己
接下来介绍一下ThreadPoolExecutor的构造方法:
从上图中可以看到,有四个构造方法,不同之处在于线程工厂和拒绝策略是你自己去指定还是使用默认的。
七个参数分别为核心线程数、最大线程数、超时时间、超时时间单位、队列、线程工厂、拒绝策略。
其中最大线程数和队列需要特别说明一下,最大线程数不是设置的越大越好,过多的线程数量会增加线程切换时间,浪费宝贵的资源,线程数量的设置和线程池处理的任务类型有关系:
cpu密集型计算推荐设置线程池核心线程数为N,也就是和cpu的线程数相同,可以尽可能低避免线程间上下文切换。
io密集型计算推荐设置线程池核心线程数为2N,但是这个数一般根据业务压测出来的,如果不涉及业务就使用推荐。
int availableProcessors = Runtime.getRuntime().availableProcessors();
使用这个方法可以得到可用的计算资源。
另外队列的长度也需要根据实际情况设定,如果不指定,默认就是Integer.MAX_VALUE,有可能会导致OOM。
线程池的一些参数解读
// 1. `ctl`,可以看做一个int类型的数字,高3位表示线程池状态,低29位表示worker数量
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// 2. `COUNT_BITS`,`Integer.SIZE`为32,所以`COUNT_BITS`为29
private static final int COUNT_BITS = Integer.SIZE - 3;
// 3. `CAPACITY`,线程池允许的最大线程数。1左移29位,然后减1,即为 2^29 - 1
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// runState is stored in the high-order bits
// 4. 线程池有5种状态,按大小排序如下:RUNNING < SHUTDOWN < STOP < TIDYING < TERMINATED
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
// 5. `runStateOf()`,获取线程池状态,通过按位与操作,低29位将全部变成0
private static int runStateOf(int c) { return c & ~CAPACITY; }
// 6. `workerCountOf()`,获取线程池worker数量,通过按位与操作,高3位将全部变成0
private static int workerCountOf(int c) { return c & CAPACITY; }
// 7. `ctlOf()`,根据线程池状态和线程池worker数量,生成ctl值
private static int ctlOf(int rs, int wc) { return rs | wc; }
线程池提交任务
直接上代码
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
// worker数量比核心线程数小,直接创建worker执行任务
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
// worker数量超过核心线程数,任务直接进入队列
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
// 线程池状态不是RUNNING状态,说明执行过shutdown命令,需要对新加入的任务执行reject()操作。
// 这儿为什么需要recheck,是因为任务入队列前后,线程池的状态可能会发生变化。
if (! isRunning(recheck) && remove(command))
reject(command);
// 这儿为什么需要判断0值,主要是在线程池构造方法中,核心线程数允许为0
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 如果线程池不是运行状态,或者任务进入队列失败,则尝试创建worker执行任务。
// 这儿有3点需要注意:
// 1. 线程池不是运行状态时,addWorker内部会判断线程池状态
// 2. addWorker第2个参数表示是否创建核心线程
// 3. addWorker返回false,则说明任务执行失败,需要执行reject操作
else if (!addWorker(command, false))
reject(command);
}
ThreadPoolExecutor的源码
addWorker源码解析
整个addWorker源码做了两步,上面两个for循环只是做了第一步,把worker的数量加1,添加一个worker。数量在32位的那个29位里面,而且是在多线程的情况下加1,所以进行了两个死循环干这个事儿外层死循环套内层死循环,上来先拿状态值,然后进行了一堆的判断,如果状态值不符合的话就return false,这个状态值加不进去,什么时候这个状态值大于shutdown,说明已经shutdown了,或者去除上面这些状态之外,所有的状态都可以往里加线程。加线程又是一个死循环,首先计算当前的wc线程数是不是超过容量了,超过容量就别加了,否则用cas的方式加,如果加成功了说明第一步完成了,就retry把整个全都break掉,外层循环内层循环一下全都跳出来了,如果没加成功就get,get完了之后重新处理,continue retry,相当于前面在不断的试,一直试到把这个数加到1为止。 然后,后面才是真真正正的启动这个work,new一个work,这个work被new出来之后启动线程,这个work代表一个线程,其实这个work类里面有一个线程,加锁,是在一个容器里面,多线程的状态是一定要加锁的,锁定后检查线程池的状态,为什么要检查,因为中间可能被其他线程干掉过,看这个状态是不是shutdown了等等,如果满足往里加的条件,加进去,加完这个线程后启动开始运行,这是addWorker的一个大体逻辑。
总的来说,addWorker做两件事儿
第一:count先加1;
第二:才是真正的加进任务去并且start;
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
// 外层自旋
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// 这个条件写得比较难懂,和下面的条件等价
// (rs >SHUTDOWN) ||
// (rs == SHUTDOWN && firstTask != null) ||
// (rs == SHUTDOWN && workQueue.isEmpty())
// 1. 线程池状态大于SHUTDOWN时,直接返回false
// 2. 线程池状态等于SHUTDOWN,且firstTask不为null,直接返回false
// 3. 线程池状态等于SHUTDOWN,且队列为空,直接返回false
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
// 内层自旋
for (;;) {
int wc = workerCountOf(c);
// worker数量超过容量,直接返回false
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
// 使用CAS的方式增加worker数量。
// 若增加成功,则直接跳出外层循环进入到第二部分
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
}
}
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;
// worker的添加必须是串行的,因此需要加锁
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)) {
// worker已经调用过了start()方法,则不再创建worker
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
// worker创建并添加到workers成功
workers.add(w);
// 更新`largestPoolSize`变量
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
// 启动worker线程
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
// worker线程启动失败,说明线程池状态发生了变化(关闭操作被执行),需要进行shutdown相关操作
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
线程池worker任务单元
这后面是work类的一个简单的解释,他的里面包了一个线程,包了一个任务,然后记录着这个work干过多少个任务了等等。
这个work他本身是Runnable同时又是AQS,关于AQS这块儿可以先忽略无所谓,因为用别的方式也能实现。本身是一个Runnable进来的任务又用这个Runnable包装了一下,为什么又要包装呢,因为它里面有好多的状态需要记录,原来这个任务里是没有的,另外这个东西必须得在线程里运行,所以用Runnable又给包装了一次。然后这个work类里面会记录着一个成员变量,这个成员变量是thread。是哪个thread正在执行这个对象呢,很多个线程会抢,所以这个就是为什么要用AQS的原因。另外,在整个执行的过程之中也需要加锁,不然的话别的线程进来,要求这个work执行其他的任务也是很有可能的 ,这个时候也需要加锁,因此AQS是需要的。这是这个work类,简单的就可以把它当成线程类,然后这个线程类执行的是自己的任务就行了。
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;
// 这儿是Worker的关键所在,使用了线程工厂创建了一个线程。传入的参数为当前worker
this.thread = getThreadFactory().newThread(this);
}
/** Delegates main run loop to outer runWorker */
public void run() {
runWorker(this);
}
// 省略代码...
}
核心线程执行逻辑-runworker
runwork是真真正正启动线程之后是怎么样去执行这个任务的,同样的,加锁。这个比较好玩的是这个work是从AbstractQueuedSynchronizer继承出来的同时实现了Runnable,说明work可以放在线程里运行,与此同时他本身就是一把锁,就可以做同步,另外,他是可以被线程执行的一个任务 ,为什么它本身就是一把锁,这个work可以认为是等着执行的一个工人,是好多个任务都可以往里面去扔内容的,也就是说会有多线程去访问这个对象的,多线程访问这个对象的时候他干脆就给自己做成了一把锁,就不要自己去定义一个lock了,所以你需要往这个work里面扔任务的时候,指定这个线程就是在执行的这个线程的时候,通过work自己去lock就可以了,完全没必要再去new别的lock,所以运行work的时候就先lock住,要run就得lock住才能执行,不然别的线程有可能把这个work给占了, 下面又是一堆的执行,执行完了之后unlock出来,执行完了之后++ 。
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
// 调用unlock()是为了让外部可以中断
w.unlock(); // allow interrupts
// 这个变量用于判断是否进入过自旋(while循环)
boolean completedAbruptly = true;
try {
// 这儿是自旋
// 1. 如果firstTask不为null,则执行firstTask;
// 2. 如果firstTask为null,则调用getTask()从队列获取任务。
// 3. 阻塞队列的特性就是:当队列为空时,当前线程会被阻塞等待
while (task != null || (task = getTask()) != null) {
// 这儿对worker进行加锁,是为了达到下面的目的
// 1. 降低锁范围,提升性能
// 2. 保证每个worker执行的任务是串行的
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();
// 执行任务,且在执行前后通过`beforeExecute()`和`afterExecute()`来扩展其功能。
// 这两个方法在当前类里面为空实现。
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 {
// 帮助gc
task = null;
// 已完成任务数加一
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
// 自旋操作被退出,说明线程池正在结束
processWorkerExit(w, completedAbruptly);
}
}
ThreadPoolExecutor里的核心源码大概就这些,再来看看AbstractExecutorService的一些源码
AbstractExecutorService源码
submit和doInvokeAny
这个抽象类里其实就是定义并实现了任务的提交和执行两大类方法,总体来说,看看submit和doInvokeAny两个就行了。
submit
submit在接受一个任务后会将它包装成一个task,然后再交给线程池自己的execute去执行。之所以需要包装成task,是因为submit是需要返回执行结果的,最终会封装成FutureTask
/** The underlying callable; nulled out after running */
private Callable<V> callable;
/** The result to return or exception to throw from get() */
private Object outcome; // non-volatile, protected by state reads/writes
这里面callable 就是将被执行的任务,outcome就是返回值
// 调用get()获取结果
public V get() throws InterruptedException, ExecutionException {
int s = state;
if (s <= COMPLETING)
s = awaitDone(false, 0L);
return report(s);
}
@SuppressWarnings("unchecked")
private V report(int s) throws ExecutionException {
Object x = outcome;
if (s == NORMAL) // 任务状态是否正常
return (V)x; // 将outcome返回
if (s >= CANCELLED)
throw new CancellationException();
throw new ExecutionException((Throwable)x);
}
doInvokeAny
这是invokeAny的核心方法,就是从队列中循环取出任务进行执行,只要有一个完成了,拿到结果并返回,最后尝试取消其他任务。另一个invokeAll()就没必要细讲了,就是执行所有的任务,然后循环拿到结果而已,唯一需要说明的就是为了防止获取结果时抛出异常,加了一个done的标识,如果出现异常,也会尝试取消其他的任务。
/**
* the main mechanics of invokeAny.
*/
private <T> T doInvokeAny(Collection<? extends Callable<T>> tasks,
boolean timed, long nanos)
throws InterruptedException, ExecutionException, TimeoutException {
if (tasks == null)
throw new NullPointerException();
int ntasks = tasks.size();
if (ntasks == 0) // 两步常规判空操作
throw new IllegalArgumentException();
ArrayList<Future<T>> futures = new ArrayList<Future<T>>(ntasks);
ExecutorCompletionService<T> ecs =
new ExecutorCompletionService<T>(this); // ExecutorCompletionService里维护了一个队列completionQueue用来记录已完成的任务
// For efficiency, especially in executors with limited
// parallelism, check to see if previously submitted tasks are
// done before submitting more of them. This interleaving
// plus the exception mechanics account for messiness of main
// loop.
try {
// Record exceptions so that if we fail to obtain any
// result, we can throw the last exception we got.
ExecutionException ee = null; // 记录异常
final long deadline = timed ? System.nanoTime() + nanos : 0L; // 用于记录任务是否超时
Iterator<? extends Callable<T>> it = tasks.iterator();
// Start one task for sure; the rest incrementally
futures.add(ecs.submit(it.next())); // 一定要开始一项任务;其余的逐渐增加 ,因为后面有一行ecs.poll(),是从队列里面获取已完成的任务
--ntasks; // 任务数量减一
int active = 1; // 处理中的任务数量
for (;;) {
Future<T> f = ecs.poll(); // 调用的是completionQueue.poll(),前面已经放进去了一个任务,这里检查那个任务是否完成
if (f == null) { // 任务没完成
if (ntasks > 0) { // 是否还有任务
--ntasks; // 任务数量减一,丢一条任务给线程池执行
futures.add(ecs.submit(it.next()));
++active; // 处理中的任务数量加一
}
else if (active == 0) // 处理中的任务数量是否为0,因为下面有一个 --active,如果为0说明有任务完成了,直接结束循环
break;
else if (timed) { //是否超时
f = ecs.poll(nanos, TimeUnit.NANOSECONDS);
if (f == null)
throw new TimeoutException();
nanos = deadline - System.nanoTime();
}
else
f = ecs.take(); // 从completionQueue中获取一个结果,如果还没有,会被阻塞
}
if (f != null) { // 获取到结果说明执行完成了
--active; // 处理中的任务数量减一
try {
return f.get(); // 获取执行结果
} catch (ExecutionException eex) {
ee = eex;
} catch (RuntimeException rex) {
ee = new ExecutionException(rex);
}
}
}
if (ee == null) // 走到这里来了肯定是有其他异常,如果没异常,前面已经return f.get() ,所以抛出一个异常
ee = new ExecutionException();
throw ee;
} finally {
// 尝试取消其他的任务,
for (int i = 0, size = futures.size(); i < size; i++)
futures.get(i).cancel(true);
}
}
public boolean cancel(boolean mayInterruptIfRunning) {
// 检查任务状态 不是新建状态的任务或者设置INTERRUPTING 或者CANCELLED失败的任务都不能取消
if (!(state == NEW &&
UNSAFE.compareAndSwapInt(this, stateOffset, NEW,
mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))//将任务状态设置为INTERRUPTING
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 {
// Removes and signals all waiting threads, invokes done(), and nulls out callable. 移除并唤醒所有等待中的线程 ,调用钩子方法done() ,并将 callable置为null以帮助jvm gc
finishCompletion();
}
return true;
}
补充一个小知识点 :interrupt(),interrupted(),isInterrupted()
interrupted() 是Thread的静态方法,判断线程是否被设置过状态标志位,调用后会将状态位复原。
interrupt() 和isInterrupted()都是线程实例的方法。
interrupt() 设置线程状态标志位,后续如果线程调用了例如sleep wait 等会抛出中断异常的方法后会中断,同时状态位被复原。
isInterrupted() 判断线程是否被设置过,不会复原状态位
如有错漏,烦请指正!