ThreadPoolExecutor源码的学习(基于JDK 1.7)
1.继承关系
ThreadPoolExecutor就是我们经常说的大名鼎鼎的线程池,Executors工厂创建的线程池都是该类的实例,通过调节参数的大小创建适用于各个场景的线程池。
通过源码我们了解到ThreadPoolExecutor继承了AbstractExecutorService,该抽象类为线程池提供了默认实现。
2.构造函数
ThreadPoolExecutor有很多重载的构造函数,所有构造函数最终都调用了一个构造函数,只是有些构造函数有默认参数而已,看下最终调用的构造函数:
这个构造函数有很多参数,我们分别来解释每一个参数的意义:
corePoolSize:核心线程数,当提交一个新的任务到线程池,如果当前线程池运行的线程数(包括闲置的线程)小于核心线程数,则会创建一个新的线程作为核心线程来执行该任务。
maximumPoolSize:线程池允许最大的线程数,当提交一个新的任务到线程池,如果当前线程池运行的线程数(包括闲置的线程)大于corePoolSize,小于maximumPoolSize,并且等待队列满的时候,会创建一个新的线程来处理该任务。
keepAliveTime:当线程池中线程数量大于corePoolSize时,闲置线程最长可以存活的时间。 unit:时间单位。
workQueue:保存任务的队列,当池中线程数大于corePoolSize时,新来的任务保存到该队列。
threadFactory:线程工厂,线程池中的线程都是通过这个工厂创建的。
handler:任务拒绝执行策略,当线程池无法处理新来任务时的处理策略。
讲到这,有必要讲下ThreadPoolExecutor的设计思路,了解了设计思路对后面源码的分析会有更好的效果。
线程池示意图:
通过线程池示意图介绍下线程池的设计思路:
当一个任务通过submit或者execute方法提交到线程池的时候,如果当前池中线程数(包括闲置线程)小于coolPoolSize,则创建一个线程执行该任务。
如果当前池中线程数大于等于coolPoolSize,则将该任务加入到等待队列。
如果任务不能入队,说明等待队列已满,若当前池中线程数小于maximumPoolSize,则创建一个临时线程(非核心线程)执行该任务。
如果当前池中线程数已经等于maximumPoolSize,此时无法执行该任务,根据拒绝执行策略处理,后面还会详细讲解具体的拒绝执行策略。
以上4步是线程池处理到达任务的主要流程。当池中线程数大于coolPoolSize,超过keepAlive时间的闲置线程会被回收掉。注意,回收的是非核心线程,核心线程一般是不会回收的。如果设置allowCoreThreadTimeOut(true),则核心线程在闲置keepAlive时间后也会被回收。任务队列是一个阻塞队列,线程执行完任务后会去队列取任务来执行,如果队列为空,线程就会阻塞,直到取到任务。
3.重要方法
1.submit方法
通常情况下我们通过submit方法向线程池提交并执行任务,线程池的submit方法都是在子类AbstractExecutorService实现的,并且有多个重载的方法,看下这些方法的实现:
这几个重载方法首先通过newTaskFor方法将任务包装成一个RunnableFuture,然后调用execute方法执行任务。
先看下newTaskFor方法:
这两个重载方法很简单,创建一个FutureTask对象返回,之后我们就可以通过这个对象的get方法获取任务执行结果了。
AbstractExecutorService这几个重载的submit方法最终都调用了execute方法,通过该方法真正执行任务。
2.execute方法
execute是真正执行任务的方法,分析execute源码之前先来看下ThreadPoolExecutor的状态字段定义:
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING,0));
//COUNT_BITS计算后等于29,活动线程数占用的位数
private static final int COUNT_BITS = Integer.SIZE - 3;
//活动线程最大数量
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
//线程池5种运行状态,保存在ctl高3位
//11111111 11111111 11111111 11111111左移29位后只保留高位3个1即:
//11100000 00000000 00000000 00000000
private static final int RUNNING = -1 << COUNT_BITS;
//0左移29位后
//00000000 00000000 00000000 00000000
private static final int SHUTDOWN = 0 << COUNT_BITS;
//1左移29位后
//00100000 00000000 00000000 00000000
private static final int STOP = 1 << COUNT_BITS;
//2左移29位后
//01000000 00000000 00000000 00000000
private static final int TIDYING = 2 << COUNT_BITS;
//3左移29位后
//01100000 00000000 00000000 00000000
private static final int TERMINATED = 3 << COUNT_BITS;
线程池维护了一个int原子变量ctl,表示线程池当前状态。通过这一个字段表示线程池当前活动线程数和线程池的运行状态。其中低29位用来表示活动线程数,高3位用来表示线程池的运行状态。
线程池的运行状态有RUNNING、SHUTDOWN、STOP、TIDYING、TERMINATED。状态所对应的数字其实没有什么意义,重点需要了解状态代表的含义。
RUNNING:该状态下的线程池可以接受新任务,并且可以处理等待队列中的任务。
SHUTDOWN:该状态下的线程池不再接受新任务,但是可以处理等待队列中的任务。
STOP:该状态下的线程池不再接受新任务,不再处理等待队列中的任务,会中断正在执行的任务。
TIDYING:所有的任务都已经中止,活动线程数为0,此状态下的线程池即将转移到TERMINATED状态。
TERMINATED:terminated()执行完后到达此状态。
线程池的状态转移包括如下几个:
RUNNING -> SHUTDOWN,在执行shutdown()方法时,线程池经历了这种状态转移过程。 RUNNING ->
STOP或者SHUTDOWN -> STOP,在执行shutdownNow()方法时,线程池经历了这种状态转移过程。 SHUTDOWN ->
TIDYING,当等待队列和池中的任务都为空时,经历了这种状态转移过程。 STOP ->
TIDYING,池中任务为空时,经历这种状态转移过程。 TIDYING ->
TERMINATED,执行terminated()方法时经历这个状态转移过程。
接下来看execute的源码:
execute的执行逻辑其实前面已经提到了,这里根据代码再分析下:
1.对于空的任务,线程池会抛出NPE异常
2.通过workerCountOf方法获取线程池的线程数,若线程数小于核心线程数,创建一个核心线程并将任务作为该核心线程的第一个任务。若创建线程失败,重新获取线程池状态。
3.尝试将任务添加到等待队列,需要注意的是,任务添加到等待队列成功后,需要进一步检查线程池状态,因为这个过程线程池的状态可能已经改变。
4.尝试将任务添加到等待队列,添加失败拒绝执行任务。
workCountOf方法很简单,通过”与”运算取ctl的低29位的值。
3.addWorker方法
//firstTask:当池中线程数小于corePoolSize或者等待队列已满,创建的工作者线程执行的第一个任务
//core:是否作为核心线程 private boolean addWorker(Runnable firstTask, boolean
core) {
retry:
for (;? {
int c = ctl.get();
//线程池当前状态
int rs = runStateOf©;
//这个条件看起来有点晕,不着急,我们仔细分析下
//原判断条件为:
//rs >= SHUTDOWN && !(rs == SHUTDOWN && firstTask == null && !workQueue.isEmpty())
//如果线程池状态为RUNNING,或者
//线程池状态是SHUTDOWN,并且firstTask为空,并且等待队列不为空,可以接受任务。
//其他情况下,addWorker直接返回false。
//通俗点讲,就是线程池处于SHUTDOWN状态时,还可以处理等待队列中的任务,但是不可以接受新任务了。
//RUNNING状态下的线程池当然可以接受新的任务了
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;? {
int wc = workerCountOf©;
//线程池中线程数量是否达到上限
//核心线程数的上限是coolPoolSize,非核心线程数的上限是maximumPoolSize
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
//增加线程数成功,结束retry对应的for循环
if (compareAndIncrementWorkerCount©)
break retry;
//重新读取状态值
c = ctl.get();
//状态改变了,到retry处重新开始for循环
if (runStateOf© != rs)
continue retry;
}
}
//到这里说明CAS增加线程数成功了
boolean workerStarted = false;
boolean workerAdded = false;
//Worker是线程池实现的内部类,实现了AQS和Runnable,包装了需要执行的任务和执行的线程
//Worker就是线程池的工作线程,是干活的工人
Worker w = null;
try {
final ReentrantLock mainLock = this.mainLock;
//创建一个工作者线程
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
mainLock.lock();
try {
//获取锁之后重新获取状态
int c = ctl.get();
int rs = runStateOf©;
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
//如果该线程已经启动了,抛出异常,因为我们稍后才会启动该线程
if (t.isAlive())
throw new IllegalThreadStateException();
//workers是线程池的私有属性,存储了Worker
workers.add(w);
int s = workers.size();
//更新线程池的最大数量
if (s > largestPoolSize)
largestPoolSize = s;
//添加成功了
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
//如果添加成功了,启动线程
t.start();
//启动成功了
workerStarted = true;
}
}
} finally {
//处理启动失败的情况,回滚,从workers中移除该worker,将wc减1
if (! workerStarted)
addWorkerFailed(w);
}
//返回添加的线程是否启动成功
return workerStarted; }
注释已经很详细了,这里再说下该方法的思路:
1.首先试图原子地增加线程数,这个过程需要检查ctl的状态,如果检查发现不能创建新worker,返回false。否则自旋CAS增加线程数,直到设置成功。
2.线程数增加成功后,真正创建worker并添加到workers工作集合中。创建worker成功后,启动该工作者线程,返回是否启动成功。如果启动worker失败,需要做回滚操作,从workers中移除该worker,并将wc减1。
4.工作线程-worker
Worker是线程池的内部类,它封装了Thread和Runnable。看下该内部类的实现:
剩下的方法都是锁操作。
前面讲到的addWorker方法,该方法创建了Worker实例并将firstTask作为Worker构造函数的参数。firstTask作为Worker第一个运行的任务。Worker构造函数创建线程的时候将firstTask作为该线程的Runnable参数。启动该线程的时候执行本Worker的run方法。run方法会调用runWorker,当线程执行完它的firstTask后会从等待队列取任务来执行,若等待队列为空,该线程就会阻塞等待,直到等待队列不空。
5.runWorker方法
runWorker方法的执行逻辑:
首先执行worker的firstTask。
从等待队列取任务执行,若取不到则阻塞等待,具体根据特定规则,后面讲到getTask方法时再介绍。
6.getTask方法
getTask的返回值表示当前worker是否需要退出,总结下该方法的逻辑:
1.如果当前已经有超过maximumPoolSize的线程,将当前线程数减1并返回null。
2.如果线程池已经处于STOP及以上的状态,将当前线程数减1并返回null。
3. 如果线程池处于SHUTDOWN状态并且等待队列为空,将当前线程数减1并返回null。
4.如果当前worker等待获取任务超时,并且允许核心线程超时退出或者当前线程数大于corePoolSize,将当前线程数减1并返回null。
runWorker执行完成后调用processWorkerExit方法执行清理工作。
7.processWorkerExit方法
processWorkerExit根据worker是否异常退出决定后续处理。
1.如果是异常退出则将ctl中的workerCount减少1,如果是正常退出的,ctl的workerCount肯定已经修正了,不需要再处理。
2.不管worker是否异常退出,worker退出后都需要记录当前线程池已经完成的任务数量,记录完成后将该worker从线程池的workers集合中移除。
3.尝试终止线程池。
4. 根据线程池的配制,决定是否增加worker以代替刚才被移除的worker。
8.tryTerminate方法
//尝试终止线程池
//该方法可能会被shutdown、shutdownNow等方法调用
final void tryTerminate() {
for (;;) {
int c = ctl.get();
/*
处于以下几个状态不能终止线程池,直接返回
1. 线程池还在运行状态,不能终止线程池。
2. 线程池的状态已经是TIDYING或者TERMINATED状态了,说明线程已经处于或即将处于终止状态。
3. 线程池的状态是SHUTDOWN,但是等待队列不为空,说明有任务还要执行。
*/
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return;
/*
如果活动线程数大于0,中断一个空闲的worker。
interruptIdleWorkers中会遍历workers,直到成功中断了一个worker。遍历过程中会判断worker
是否被锁住,如果锁住说明该worker目前正忙,不可以中断,否则说明该worker处于idle状态,尝试中断。
*/
if (workerCountOf(c) != 0) {
interruptIdleWorkers(ONLY_ONE);
//活动线程数大于0,不能终止线程池,直接返回
return;
}
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//尝试将ctl设置为TIDYING状态
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
//空方法,留给子类实现
terminated();
} finally {
//ctl最终状态置为TERMINATED,线程数置为0
ctl.set(ctlOf(TERMINATED, 0));
//已经终止线程了,唤醒在termination上等待的线程
//线程池的awaitTermination方法会在该条件等待
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
}
}
9.RejectedExecutionHandler
execute不能执行任务时调用reject方法处理。reject的处理逻辑是调用拒绝执行处理器的rejectedExecution方法:
final void reject(Runnable command) {
handler.rejectedExecution(command, this);
}
可以通过线程池的构造函数或者setRejectedExecutionHandler方法设置拒绝执行处理器,如果不设置,会有默认的处理器:AbortPolicy。
下面详细介绍线程池的拒绝执行策略,拒绝执行处理器是一个接口,即:RejectedExecutionHandler,该接口仅有一个方法rejectedExecution,看下该接口的源码:
public interface RejectedExecutionHandler {
void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
线程池实现了四种拒绝执行策略,且都是通过ThreadPoolExecutor的静态内部类实现,这四种拒绝策略分别是:
1.AbortPolicy:这也是线程池的默认拒绝执行策略。该策略仅会抛出RejectedExecutionException异常,是最简单的策略。
2.DiscardPolicy:这个策略的实现其实什么也没做,什么也没有做意味着简单的丢弃任务,也是一个简单的拒绝策略。
3.CallerRunsPolicy:调用方执行策略,既然线程池中没有线程可以执行该任务,那就让运行线程池的线程来执行该任务。
看下该类的实现:
public static class CallerRunsPolicy implements RejectedExecutionHandler {
public CallerRunsPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
//调用r的run方法,这是由运行线程池的线程来运行
r.run();
}
}
}
4.DiscardOldestPolicy:丢弃等待队列中最老的任务,并执行新的任务。看下该类的实现:
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
public DiscardOldestPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
//丢弃队列中最老的任务后立即执行任务r
e.getQueue().poll();
e.execute(r);
}
}
}
10.shutdown方法
shutdown方法会关闭线程池,不再接受新的任务,但是等待队列中的任务还是会执行的。
11.shutdownNow方法
shutdownNow方法关闭线程池,停止所有正在执行的任务,移除等待队列中的任务。
12.allowCoreThreadTimeout方法
该方法允许核心线程超时退出,默认情况下,核心线程超时是不会退出的。
13.invokeAll方法
该方法是子类AbstractExecutorService的方法,批量提交并执行任务,等待所有任务执行完成该方法才会返回。