ThreadPoolExecutor类结构
线程池执行流程
- 线程池判断核心线程是否都处于运行状态,如果不是,就创建一个新线程来执行任务。如果是,执行2。
- 判断线程池中的工作队列是否已经满,如果未满,那么直接添加到工作队列,如果满了就执行3。
- 线程池判断池中的线程是否处于工作状态。如果没有就创建一个新工作线程执行任务。如果已经满了,则执行饱和策略的任务。
ThreadPoolExecutor源码分析
关键属性:
// 该变量ctl作为线程池中的重要属性,属性一分为2;
// 高位(前三位)用来保存各个线程的状态,低位(后三位)保存有效线程的数量。
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// 线程数量占用的位数
private static final int COUNT_BITS = Integer.SIZE - 3;
// 最大线程容量为2的29次幂减1
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// 接受新任务并处理排队任务
private static final int RUNNING = -1 << COUNT_BITS;
// 不接受新任务,而是处理队列任务
private static final int SHUTDOWN = 0 << COUNT_BITS;
// 不接受新任务,不处理排队任务和中断进程中的任务
private static final int STOP = 1 << COUNT_BITS;
// 终止所有的任务,有效的线程数量设置为0,TIDYING状态的线程执行回调方法terminated()
private static final int TIDYING = 2 << COUNT_BITS;
// terminated()方法已经执行完毕
private static final int TERMINATED = 3 << COUNT_BITS;
// 持有工作线程队列
private final BlockingQueue workQueue;
// 工作线程所在的集合上的锁
private final ReentrantLock mainLock = new ReentrantLock();
// 包含所有工作线程的集合,仅在获取到锁时才可以访问
private final HashSet workers = new HashSet();
// 使用等待队列
private final Condition termination = mainLock.newCondition();
// 最大池的大小
private int largestPoolSize;
// 完成任务时的计数值,只能在终止工作线程时更新,在获取到锁时才可以访问
private long completedTaskCount;
// 创建新线程的工厂
private volatile ThreadFactory threadFactory;
// 关闭执行的线程池时调用的Handler
private volatile RejectedExecutionHandler handler;
// 等待工作的线程的超时时间,单位纳秒
private volatile long keepAliveTime;
// 是否允许核心线程等待,
// 默认false,就是空闲也可以生存。
// true的话,在keepAliveTime时间内生存。
private volatile boolean allowCoreThreadTimeOut;
// 核心运行的线程数
private volatile int corePoolSize;
// 最大线程数,超出这个数量将执行丢弃策略
private volatile int maximumPoolSize;
// 默认拒绝执行策略Handler
private static final RejectedExecutionHandler defaultHandler =
new AbortPolicy();
关键方法:
private static int runStateOf(int c) { return c & ~CAPACITY; }
说明:作用就是获取线程池的状态;过程:~CAPACITY :按位取反的值为11100000000000000000000000000000。c & ~CAPACITY : 执行按位与操作,那么结果的低29位肯定为0。
private static int workerCountOf(int c) { return c & CAPACITY; }
说明:获取线程池工作线程的数量;过程:CAPACITY : 00011111111111111111111111111111。&操作将参数的高3位设置0。
private static int ctlOf(int rs, int wc) { return rs | wc; }
说明:将runState和workerCount存到同一个int中。使用或运算,将两个值合并。
// 使用cas方式将线程的数量加1
private boolean compareAndIncrementWorkerCount(int expect) {
return ctl.compareAndSet(expect, expect + 1);
}
// 使用cas方式将线程的数量减1
private boolean compareAndDecrementWorkerCount(int expect) {
return ctl.compareAndSet(expect, expect - 1);
}
// 减少ctl的计数值
private void decrementWorkerCount() {
do {} while (! compareAndDecrementWorkerCount(ctl.get()));
}
详解execute方法
- 说明(不关心线程池的状态和能否添加到等待队列):
① 如果当前工作线程数小于核心线程数,委派给addWorker方法(将封装线程的Worker添加到Worker集合中,然后启动线程)。
② 如果当前工作线程数大于核心线程数,将工作线程command添加到等待队列。
addWorker方法:将封装线程的Worker添加到Worker集合中,然后启动线程。
// 添加任务
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
// 死循环遍历
for (;;) {
// 获取当前的线程池属性值
int c = ctl.get();
// 获取线程池的状态
int rs = runStateOf(c);
// 如果线程池不可以接受任务
if (rs >= SHUTDOWN && ! (rs == SHUTDOWN &&
firstTask == null && ! workQueue.isEmpty()))
return false;
// 死循环遍历
for (;;) {
// 获取工作线程的数量
int wc = workerCountOf(c);
// 如果工作线程的数量超出线程池最大线程容量
// 或者工作线程的数量超出核心线程池数量或者最大线程池的数量
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
// 采用cas方法更新线程状态值,如果成功,那么worker数量加1
if (compareAndIncrementWorkerCount(c))
break retry;
// 重新读取线程池属性值
c = ctl.get(); // Re-read ctl
// 如果线程池的状态不等于之前的线程状态,重新执行retry代码块
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 {
// 获取线程池的锁
final ReentrantLock mainLock = this.mainLock;
// 将firstTask封装成worker
w = new Worker(firstTask);
// 获取worker的工作线程
final Thread t = w.thread;
// 如果工作线程不为空
if (t != null) {
// 给线程池上锁
mainLock.lock();
try {
// 获取当前的线程池的属性值
int c = ctl.get();
// 获取当前线程池的状态
int rs = runStateOf(c);
// 如果线程池的状态可以接受任务正常工作
// 或者如果线程池不接受任务并且当前任务为空
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
// 判断工作线程是否可以启动
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
// 将当前的工作线程添加到workers集合中
workers.add(w);
// 获取Worker工作线程的数量
int s = workers.size();
// 如果Worker工作线程的数量超出最大池长度
if (s > largestPoolSize)
// 更新最大池长度
largestPoolSize = s;
// 设置工作线程添加成功
workerAdded = true;
}
} finally {
// 线程池释放锁
mainLock.unlock();
}
// 如果工作线程添加成功
if (workerAdded) {
// 启动工作线程
t.start();
// 设置工作线程启动成功
workerStarted = true;
}
}
} finally {
// 如果工作线程启动失败!
if (! workerStarted)
// 回滚Worker线程的创建
addWorkerFailed(w);
}
// 工作线程启动成功标识
return workerStarted;
}
说明(代码分为两个大块逻辑):
- 第一大块,两个死循环:可以发现代码结构是两个死循环,外循环的作用主要是检查当前线程池的状态,如果线程池不可以接受任务,那么直接退出。内循环的作用主要是检查工作线程的数量。
① 如果线程池不可以接受任务,那么直接退出。
② 如果工作线程的数量大于最大线程容量或者工作线程的数量大于核心线程池数量(最大线程池的数量),那么直接退出。
③ 使用cas更新线程属性值ctl(将ctl值加1),因为ctl的低位用来存储工作线程的数量,所以就是cas方式更新工作线程加1,跳出循环。如果线程池的状态和之前的不一致,说明cas方式失败了,那么重新执行retry。 - 第二大块,将当前的工作线程Worker添加到集合,然后启动线程。
addWorkerFailed方法:线程添加失败策略。
工作线程Worker类设计
前面源码设计,主要还是使用当前工作线程Worker对象,设计在线程池中执行的流程,然后前面addWorker方法里面,启动线程,执行Worker的run方法,现在进入Worker类里面,看看源码怎么设计的?
Worker类:封装了需要执行的线程类
Worker实现了Runnable接口,封装了线程的实现和执行。并且继承了AQS队列同步器。简化了获取锁和释放锁的过程,交给AQS去实现了。
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable {
private static final long serialVersionUID = 6138294804551838833L;
// 运行中的线程,由ThreadFactory工厂创建
final Thread thread;
// 第一个执行的任务,可能为null
Runnable firstTask;
// 线程计数器,记住完成的任务
volatile long completedTasks;
// 使用线程工厂ThreadFactory为第一个任务firstTask创建线程
Worker(Runnable firstTask) {
// 在执行runWorker之前,禁止中断操作
setState(-1); // inhibit interrupts until runWorker
// 初始化第一个执行的任务
this.firstTask = firstTask;
// 获取线程工厂,为当前对象创建一个线程来初始化
this.thread = getThreadFactory().newThread(this);
}
// 运行线程run方法委托给外部的runWorker来执行
public void run() {
runWorker(this);
}
// 判断当前线程是否独占(使用state状态值判断,如果获取锁就加1,如果释放锁就减1)
protected boolean isHeldExclusively() {
return getState() != 0;
}
// 当前线程尝试获取锁
protected boolean tryAcquire(int unused) {
// 使用cas的方式来设置当前线程获取锁
if (compareAndSetState(0, 1)) {
// 设置当前线程为独占线程
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
// 当前线程尝试释放锁
protected boolean tryRelease(int unused) {
// 将独占线程设置为null
setExclusiveOwnerThread(null);
// 使用cas方式更新当前同步状态值为0
setState(0);
return true;
}
// 独占式获取锁
public void lock() { acquire(1); }
// 尝试以独占式获取锁
public boolean tryLock() { return tryAcquire(1); }
// 独占式释放锁
public void unlock() { release(1); }
// 判断当前线程是否独占着锁
public boolean isLocked() { return isHeldExclusively(); }
// 线程启动后中断
void interruptIfStarted() {
Thread t;
// 标识线程中断
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
}
说明:
- 里面大多实现的方法,获取锁,释放锁,判断当前线程是否中断都是AQS的cas操作同步状态值state的方式。
AbstractQueuedSynchronizer同步队列源码 - Worker本身被设计为一个线程类,需要初始化第一个执行的线程以及线程Thread对象,线程执行run方法的逻辑委派给外部的runWorker方法。
runWorker方法:Worker的run线程执行的方法;就是执行线程任务。
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 ?
// 超时keepAliveTime秒释放当前工作线程
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) {
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
// 工作线程数量减1
decrementWorkerCount();
// 获取重入锁
final ReentrantLock mainLock = this.mainLock;
// 上锁
mainLock.lock();
try {
// 完成任务的数量增加
completedTaskCount += w.completedTasks;
// 工作线程集合移除工作线程
workers.remove(w);
} finally {
// 释放锁
mainLock.unlock();
}
// 终止线程池
tryTerminate();
// cas方式获取ctl值
int c = ctl.get();
// 如果当前线程池接受新任务且处理排队任务
// 或者线程池虽然不接受新任务但是还处理排队任务
if (runStateLessThan(c, STOP)) {
if (!completedAbruptly) {
// min 线程池最小空闲数
// allowCoreThreadTimeOut允许核心线程在keepAliveTime时间等待之后是否允许生存
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
// 如果min == 0但是队列不为空要保证有1个线程来执行队列中的任务
if (min == 0 && ! workQueue.isEmpty())
min = 1;
// 如果线程池中工作线程不为空,就直接返回了
if (workerCountOf(c) >= min)
return; // replacement not needed
}
// 线程池将不处理队列中的任务并且等待队列不为空,直接返回false
addWorker(null, false);
}
}
shutdown方法:将线程池的状态转换为SHUTDOWN并且终止所有线程
// 将线程池的状态转换为SHUTDOWN并且终止所有线程
public void shutdown() {
// 获取重入锁
final ReentrantLock mainLock = this.mainLock;
// 上锁
mainLock.lock();
try {
// 检查线程池关闭的权限
checkShutdownAccess();
// 将当前线程池的状态转换到目标状态targetState
advanceRunState(SHUTDOWN);
// 中断等待队列中的空闲线程
interruptIdleWorkers();
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
// 释放锁
mainLock.unlock();
}
// 将线程池的状态转换为终止状态
tryTerminate();
}
// 将当前线程池的状态转换到目标状态targetState
private void advanceRunState(int targetState) {
for (;;) {
int c = ctl.get();
if (runStateAtLeast(c, targetState) ||
ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
break;
}
}
线程回收
问题:如果我们不设置线程池回收线程,那么线程池中的线程会怎么样。
public static void main(String[] args) {
ThreadPoolExecutor tp = new ThreadPoolExecutor(3,
5,
1,
TimeUnit.SECONDS,
new ArrayBlockingQueue(10),
new ThreadPoolExecutor.CallerRunsPolicy());
for (int i = 0 ; i < 5; i++) {
tp.execute(new Runnable() {
public void run() {
System.out.println(Thread.currentThread().getName());
}
});
}
}
说明:线程池中的线程执行完任务后,当从等待队列中获取任务时,因为任务已经执行完了,没有任务了,阻塞的工作队列已经空了,那么线程只能处于阻塞状态了(这段答案在上面getTask方法的源码)。
allowCoreThreadTimeOut方法:设置线程超时回收(包括核心线程和非核心线程)
// 设置线程超时回收(包括核心线程和非核心线程)
public void allowCoreThreadTimeOut(boolean value) {
// 如果keepAliveTime设置不大于0,那么报出非法参数异常
if (value && keepAliveTime <= 0)
throw new IllegalArgumentException("Core threads must have nonzero keep alive times");
// value为true时向下执行,allowCoreThreadTimeOut默认值为false
if (value != allowCoreThreadTimeOut) {
// allowCoreThreadTimeOut设置为true
allowCoreThreadTimeOut = value;
if (value)
// 中断等待任务的线程
interruptIdleWorkers();
}
}
// 中断等待任务的线程
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()) {
try {
// 将线程中断
t.interrupt();
} catch (SecurityException ignore) {
} finally {
// 释放锁
w.unlock();
}
}
if (onlyOne)
break;
}
} finally {
// 释放锁
mainLock.unlock();
}
}
说明:通过设置allowCoreThreadTimeOut为true,线程池中的线程如果阻塞的时间超过了keepAliveTime,那么将被标记为中断标志。
还可以通过前面提到的关闭线程池shutdown方法:检查线程池关闭的权限、将当前线程池状态转为为SHUTDOWN、中断等待(阻塞)的线程。
源码阅读总结
1. 线程池采用ThreadFactory中默认的DefaultThreadFactory实现类来创建的,使用工厂方法模式来设计的线程工厂创建线程。(而工厂方法模式对于我的理解:一个产物对应一个工厂)
2. 线程池的任务饱和策略?
AbortPolicy:直接抛出RejectedExecutionException异常。
CallerRunsPolicy:只用调用者所在线程来运行任务。
DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。
DiscardPolicy:不处理,丢弃掉。
3. 线程池什么时候启动?
答案:在new ThreadPoolExecutor类时并没有启动线程池,只是设置了参数,而线程池的启动是在执行execute方法(源码中是addWorker方法)。
4.线程池执行任务的逻辑(execute方法的执行逻辑)?
4.1. 工作线程数 < 核心线程数(调用addWorker方法)
4.1.1 new Thread创建新线程作为当前工作线程,将当前工作线程Worker添加到workers集合
4.1.2 启动当前线程(调用runWorker方法)
4.1.2.1 获取当前线程,当前线程已执行完毕并释放,那么从阻塞队列获取任务(调用getTask)
4.1.2.1.1 如果等待队列中存在了等待的任务,那么取出等待队列中第一个任务返回。
4.1.2.1.2 如果超过核心线程数的线程,那么timed = true,同时队列中已经没有任务了,那么等待keepalivetime秒就释放。
4.1.2.1.3 如果不超过(等于)核心线程数的线程,那么timed=false,同时队列中已经没有任务了,那么执行take方法阻塞。
4.1.2.1.3.1 如果此时进来一个新任务,那么发现阻塞线程数(工作线程数)不小于核心线程数,那么直接执行offer插入等待队列,等待队列有值了,阻塞的线程们又开始抢任务干活了。
4.1.2.2 执行我们自己的业务逻辑(调用run方法)
4.1.2.3 移除workers中的当前线程,回收线程(调用processWorkerExit方法)
4.1.3 移除workers中的当前线程,回收线程(调用addWorkerFailed方法)
4.2. 工作线程数 > 核心线程数
4.2.1 将当前线程加入到等待队列BlockQueue
4.3. 如果等待队列满了
4.3.1 直接创建新线程作为工作线程执行逻辑。
4.4. 如果当前工作线程数 > 最大线程数
4.4.1 抛出拒绝策略异常
5. 线程释放策略?
①第一种策略:使用allowCoreThreadTimeOut方法对核心线程和非核心线程进行释放(调用poll方法,超出了keepAliveTime,自动释放)。
②第二种策略:使用shutdown方法关闭线程池来对核心线程和非核心线程进行释放(主动为Workers集合中的线程设置中断标识,自动释放)。
6. 线程回收处理?
getTask中使用cas自旋,达到-1时,退出方法,回收线程。
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
// 工作线程的数量减1
decrementWorkerCount();
return null;
}
private void decrementWorkerCount() {
do {} while (! compareAndDecrementWorkerCount(ctl.get()));
}
private boolean compareAndDecrementWorkerCount(int expect) {
return ctl.compareAndSet(expect, expect - 1);
}