ThreadPoolExecutor核心分析
1、应用方式
- 只需要构建好
ThreadPoolExecutor
对象即可,传入指定参数- 通过
execute
可以把 Runnable 扔进线程池- 通过
submit
既可以把 Runnable 扔进线程池,也能把 Callable 扔进线程池,且 Callable 可接返回值
import java.util.concurrent.*;
/**
* 自定义线程池
*/
public class 自定义线程池 {
public static void main(String[] args)
throws ExecutionException, InterruptedException {
/**
* 1、创建自定义线程池
*/
ThreadPoolExecutor myPool = new ThreadPoolExecutor(
10, // corePoolSize:核心线程数
20, // maximumPoolSize:最大线程数
0L, // keepAliveTime:存活时间
TimeUnit.MILLISECONDS, // unit:时间单位
new ArrayBlockingQueue<Runnable>(10), // workQueue:等待队列
new ThreadFactory(){
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setName("xxx");
return t;
}
}, // threadFactory 线程工程
new RejectedExecutionHandler(){
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println("拒绝!不鸟你!");
}
} // handler 拒绝策略
);
for (int i = 0; i < 1000; i++) {
/**
* 2.1、通过 execute 将线程丢进池子执行(只能丢Runnable)
*/
int finalI = i;
myPool.execute(()->{
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "---" + finalI);
});
/**
* 2.2、通过 submit 将线程丢进池子执行
*(既能丢Runnable也能丢Callable,且Callable可接返回值)
*/
Future<Object> result = myPool.submit(new Callable<Object>() {
@Override
public Object call() throws Exception {
return finalI;
}
});
System.out.println("---< 通过 Future 获取的返回值 >--->" + result.get());
}
/**
* 3、关闭线程池
*/
myPool.shutdown();
}
}
2、核心参数
int corePoolSize, // 核心线程数
int maximumPoolSize, // 最大线程数
long keepAliveTime, // 存活时间
TimeUnit unit, // 时间单位
BlockingQueue<Runnable> workQueue, // 阻塞队列
ThreadFactory threadFactory, // 线程工厂
RejectedExecutionHandler handler // 拒绝策略
常用 workQueue:
public interface Queue<E> extends Collection<E> {
/**
* 插入》》》
* 如果可以在不违反容量限制的情况下立即将指定元素插入此队列,则在成功时返回{@code true},
* 如果当前没有可用空间,则抛出{@code IllegalStateException}
*
* @param e 要添加的元素
* @return {@code true} (由{@link Collection#add}指定)
* @throws IllegalStateException 如果由于容量限制,此时无法添加元素
* @throws ClassCastException 如果指定元素的类阻止将其添加到此队列
* @throws NullPointerException 如果指定的元素为空,并且此队列不允许空元素
* @throws IllegalArgumentException 如果此元素的某些属性阻止将其添加到此队列
*/
boolean add(E e);
/**
* 插入》》》
* 如果可以在不违反容量限制的情况下立即将指定元素插入此队列,则将其插入。当使用容量受限队列时,
* 此方法通常优于{@link#add},后者只能通过抛出异常来插入元素.
*
* @param e 要添加的元素
* @return {@code true} 如果元素已添加到此队列,否则 {@code false}
* @throws ClassCastException 如果指定元素的类阻止将其添加到此队列
* @throws NullPointerException 如果指定的元素为空,并且此队列不允许空元素
* @throws IllegalArgumentException 如果此元素的某些属性阻止将其添加到此队列
*/
boolean offer(E e);
/**
* 头删 ×××
* 检索并删除此队列的头。此方法与{@link#poll poll}的不同之处在于,如果此队列为空,它将引发异常.
*
* @return 这个队列的头
* @throws NoSuchElementException 如果此队列为空
*/
E remove();
/**
* 头删 ×××
* 检索并删除此队列的头,如果此队列为空,则返回{@code null}.
*
* @return 此队列的头,如果此队列为空,则为{@code null}
*/
E poll();
/**
* 《《《获取头
* 检索但不删除此队列的头。此方法与{@link#peek peek}的不同之处在于,如果此队列为空,它将引发异常.
*
* @return 这个队列的头
* @throws NoSuchElementException 如果此队列为空
*/
E element();
/**
* 《《《获取头
* 检索但不删除此队列的头,如果此队列为空,则返回{@code null}.
*
* @return 此队列的头,如果此队列为空,则为{@code null}
*/
E peek();
}
默认提供的 4个 handler:
public class ThreadPoolExecutor extends AbstractExecutorService {
/* 预定义的 RejectedExecutionHandler */
/* 我不管,谁扔进来的谁运行! */
public static class CallerRunsPolicy implements RejectedExecutionHandler {
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
/* 直接原地爆炸! */
public static class AbortPolicy implements RejectedExecutionHandler {
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
/* 装傻 */
public static class DiscardPolicy implements RejectedExecutionHandler {
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
/* 偷摸把列头扔了,尝试新任务的重入 */
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
}
3、执行流程
4、状态
1、线程池核心属性 ctl
public class ThreadPoolExecutor extends AbstractExecutorService {
/**
* 主池控制状态ctl是一个包含两个概念字段的原子整数
* workerCount:指示有效线程数
* runState:指示是否正在运行、关闭等
*
* 为了将它们打包成一个整数,我们将 workerCount 限制为
* (2^29)-1(约5亿)个线程,而不是(2^31)-1(20亿)个其他可
* 表示的线程。如果将来出现问题,可以将变量更改为原子长,并调整下面
* 的 移位/掩码 常数。但是,在需要之前,使用int会使代码更快、更简单
*
* workerCount 是允许启动和不允许停止的工人数。该值可能暂时不同于
* 活动线程的实际数量,例如,当 ThreadFactory 在被请求时无法创建
* 线程时,以及当退出线程在终止之前仍在执行簿记时。用户可见池大小报
* 告为工作集的当前大小
*
* 运行状态提供主要的生命周期控制,具有以下值:
* RUNNING: 接受新任务并处理排队的任务
* SHUTDOWN: 不接受新任务,但处理排队的任务
* STOP: 不接受新任务,不处理排队的任务,
* 以及中断正在进行的任务
* TIDYING: 所有任务都已终止, workerCount 为零,
* 转换到状态“TIDYING”的线程将运行
* terminated()钩子方法
* TERMINATED: terminated() 已经完成
*
* 这些值之间的数字顺序很重要,以便进行有序比较。运行状态随时间单调增加,
* 但不需要达到每个状态。过渡是:
* RUNNING -> SHUTDOWN
* 在调用 shutdown() 时,可能隐式地在 finalize() 中
* (RUNNING or SHUTDOWN) -> STOP
* 在调用 shutdownNow() 时
* SHUTDOWN -> TIDYING
* 当 queue 和 pool 都为空时
* STOP -> TIDYING
* 当 pool 为空时
* TIDYING -> TERMINATED
* 当 terminated() 钩子方法完成时
*
* 等待 awaitTermination() 的线程将在状态达到 TERMINATED 时返回
*
* 检测从 SHUTDOWN 到 TIDYING 的转换并不像您希望的那样简单,因为队列
* 在非空状态后可能会变为空,而在 SHUTDOWN 状态下也可能变为空。但我们
* 只能在看到队列为空后,看到 workerCount 为 0 时终止(这有时需要重新
* 检查——见下文)
*/
// ctl 本质就是一个 int 类型的数值
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// 【常量 29】 : COUNT_BITS = (Integer.SIZE = 32) - 3
// ctl 表示两个状态:
// 1、表示线程池当前状态(高3位)
// 2、表示线程池当前的工作线程个数(低29位)
private static final int COUNT_BITS = Integer.SIZE - 3;
// 低29位 存储 【当前的工作线程个数】 最多 2^29 -1
/**
* 1 << 29 = 0010 0000 0000 0000 0000 0000 0000 0000
* - 1 = 0001 1111 1111 1111 1111 1111 1111 1111
* 【高3位 = 111 ==> RUNNING】
*/
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// 高3位 存储 运行状态
/**
* -1 = 1111 1111 1111 1111 1111 1111 1111 1111
* -1 << 29 = 1110 0000 0000 0000 0000 0000 0000 0000
* 【高3位 = 111 ==> RUNNING】
*/
private static final int RUNNING = -1 << COUNT_BITS;
/**
* 0 = 0000 0000 0000 0000 0000 0000 0000 0000
* 0 << 29 = 0000 0000 0000 0000 0000 0000 0000 0000
* 【高3位 = 000 ==> SHUTDOWN】
*/
private static final int SHUTDOWN = 0 << COUNT_BITS;
/**
* 1 = 0000 0000 0000 0000 0000 0000 0000 0001
* 1 << 29 = 0010 0000 0000 0000 0000 0000 0000 0000
* 【高3位 = 001 ==> SHUTDOWN】
*/
private static final int STOP = 1 << COUNT_BITS;
/**
* 2 = 0000 0000 0000 0000 0000 0000 0000 0010
* 2 << 29 = 0100 0000 0000 0000 0000 0000 0000 0000
* 【高3位 = 010 ==> TIDYING】
*/
private static final int TIDYING = 2 << COUNT_BITS;
/**
* 3 = 0000 0000 0000 0000 0000 0000 0000 0011
* 3 << 29 = 0110 0000 0000 0000 0000 0000 0000 0000
* 【高3位 = 011 ==> TERMINATED】
*/
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; }
}
2、线程池状态变换
5、execute
第一点核心:通过 execute方法,查看到线程池的整体执行流程,以及一些避免并发情况的判断
第二点核心:为什么线程池会添加一个空任务的非核心线程到线程池
public class ThreadPoolExecutor extends AbstractExecutorService {
/**
* 在将来的某个时间执行给定的任务。该任务可以在新线程或现有池线程中执行。
*
* 如果由于此执行器已关闭或已达到其容量而无法提交任务以供执行,则该任务将
* 由当前{@code RejectedExecutionHandler}处理。
*
* @param command 要执行的任务
* @throws RejectedExecutionException 如果无法接受任务执行,则由
* {@code RejectedExecutionHandler}自行决定
* @throws NullPointerException 如果{@code command}是null
*/
public void execute(Runnable command) {
if (command == null) // 非空判断
throw new NullPointerException();
/*
* 分3步进行:
*
* 1. 如果运行的线程少于corePoolSize,请尝试使用给定命令作为其第一
* 个任务启动新线程。对addWorker的调用以原子方式检查 runState 和
* workerCount,因此通过返回false来防止在不应该添加线程时添加线程
* 的错误警报.
*
* 2. 如果一个任务可以成功地排队,那么我们仍然需要仔细检查是否应该添
* 加一个线程(因为自上次检查以来存在已有的线程已死亡),或者自进入此
* 方法后池是否已关闭。因此,我们重新检查状态,如果停止,则在必要时回
* 滚排队,如果没有,则启动新线程.
*
* 3. 若我们无法将任务排队,那个么我们将尝试添加一个新线程。如果失败,
* 我们知道我们已关闭或饱和,因此拒绝该任务.
*/
int c = ctl.get(); // 获取 ctl 属性
// 工作线程的个数是否小于核心线程数
if (workerCountOf(c) < corePoolSize) {
// 通过 addWorker 添加一个核心线程去执行 command 任务
if (addWorker(command, true))
// 添加核心线程成功返回true,直接return
return;
// 添加核心线程失败,重新获取 ctl 属性
c = ctl.get();
}
// 这里往下是创建核心线程失败的情况
// 通过 isRunning 判断当前线程池状态是否是RUNNING
// 如果是RUNNING,将执行offer将任务添加到工作队列(成功返回true,失败返回false)
if (isRunning(c) && workQueue.offer(command)) {
// 任务添加到工作队列成功,重新获取 ctl 属性
int recheck = ctl.get();
// 判断当前线程池状态时都是非RUNNING(>=SHUTDOWN),尝试从工作队列移除任务
if (! isRunning(recheck) && remove(command))
// 线程池状态不正确,执行拒绝策略
reject(command);
// 状态是RUNNING或者工作队列移除任务失败,判断工作线程是否为0
else if (workerCountOf(recheck) == 0)
// 如果是0说明刚扔进去的任务(移除还失败了),但是没有工作线程执行这个任务
// 添加一个空任务非核心线程,为了处理工作队列中排队的任务
addWorker(null, false);
}
// 当前不是RUNNING状态或者任务添加到工作队列失败,尝试添加非核心线程执行当前任务
else if (!addWorker(command, false))
// 创建非核心线程失败执行拒绝策略
reject(command);
}
private static boolean isRunning(int c) {
return c < SHUTDOWN;
}
}
6、addWorker
添加工作线程并启动工作线程
public class ThreadPoolExecutor extends AbstractExecutorService {
/* 锁. */
private final ReentrantLock mainLock = new ReentrantLock();
/**
* 包含池中所有工作线程的集合。仅在持有mainLock时访问.
*/
private final HashSet<Worker> workers = new HashSet<Worker>();
/**
* 跟踪达到的最大池大小。仅在mainLock下访问.
*/
private int largestPoolSize;
/**
* 添加工作线程。
*
* 检查是否可以根据当前池状态和给定绑定(核心或最大值)添加新工作线程。
* 如果是,将相应地调整工作人员计数,如果可能,将创建并启动一个新的
* 工作人员,并将firstTask作为其第一个任务运行。如果池已停止或符合
* 关闭条件,则此方法返回false。如果线程工厂在被请求时未能创建线程,
* 它也会返回false。如果线程创建失败,无论是由于线程工厂返回null,
* 还是由于异常(通常是 Thread.start() 时的 OutOfMemoryError),
* 我们都会完全回滚.
*
* 入参:firstTask 新线程应首先运行的任务(如果没有,则为空)。
* 工作线程是用初始的第一个任务(在方法execute()中)
* 创建的,以在少于corePoolSize线程(在这种情况下,
* 我们总是启动一个)或队列已满(在这种情形下,我们必
* 须绕过队列)时绕过队列。最初,空闲线程通常是通过
* prestartCoreThread创建的,或者用来替换其他正
* 在死亡的工作线程.
*
* 入参:core 如果为true,则使用corePoolSize作为绑定,否则使用
maximumPoolSize。(此处使用布尔指示符,而不是值,
* 以确保在检查其他池状态后读取新值。)
*
* 返回:true 如果成功
*/
private boolean addWorker(Runnable firstTask, boolean core) {
// ⑴ 对线程池状态的判断,以及对工作线程数量的判断
// 外层for循环标识
retry:
for (;;) {
// 获取 ctl 的值
int c = ctl.get();
// 拿到线程池的状态
int rs = runStateOf(c);
// 如果状态非RUNNING,继续后续判断,验证当前任务是否需要被处理
if (rs >= SHUTDOWN &&
//!( 状态SHUTDOWN,任务=null,工作队列非空;这里映射execute.addWorker(null, false))
! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty()))
// 线程池不是RUNNING状态,不接收新任务
// 不符合execute.addWorker(null, false),不走当前位置
return false;
// 外层for判断状态↑↑↑,内层for判断数量↓↓↓
for (;;) {
// 基于ctl获取当前工作线程数
int wc = workerCountOf(c);
// 如果工作线程数>=最大值
if (wc >= CAPACITY ||
// 核心是否达到最大核心数,非核心是否达到最大线程数
wc >= (core ? corePoolSize : maximumPoolSize))
// 工作线程已经达到最大值了不能再创新新线程了直接返回false
return false;
// 以CAS的方式,对工作线程数+1,如果成功直接跳出外层for循环
if (compareAndIncrementWorkerCount(c))
break retry;
// 重新获取 ctl (走到这里说明当前工作线程数+1失败了,就是并发竞争一个成功其余失败了)
c = ctl.get();
// 基于新ctl获取线程池状态判断其是否和之前的rs状态一致
if (runStateOf(c) != rs)
// 不一致说明并发操作导致线程池状态发生了变化,需要跳回外层for循环重新判断状态
continue retry;
// 否则,由于workerCount更改,CAS失败;重试内部循环
}
}
// ⑵ 添加工作线程并启动工作线程
boolean workerStarted = false; // 启动标识
boolean workerAdded = false; // 添加标识
Worker w = null; // worker对象,就是工作线程
try {
// 创建工作线程,并把任务扔进Worker
w = new Worker(firstTask);
// 拿到 Worker 中绑定的 Thread 线程
final Thread t = w.thread;
// 肯定不为null!!!why?刚特么new的你说它怎么null?!!!(健壮性判断)
if (t != null) {
// 获取this的锁
final ReentrantLock mainLock = this.mainLock;
// 加锁,防止并发冲突
mainLock.lock();
try {
// 基于新获取的 ctl 拿到线程池状态
int rs = runStateOf(ctl.get());
// 线程池状态是RUNNING
if (rs < SHUTDOWN ||
// 线程池状态是SHUTDOWN且任务为null,映射execute.addWorker(null, false)
(rs == SHUTDOWN && firstTask == null)) {
// 开始添加工作线程的步骤
// 判断t是否已经处于run状态(健壮性判断)
if (t.isAlive())
throw new IllegalThreadStateException();
// 将构建好的Worker对象添加到workers
workers.add(w);
// 获取工作线程个数
int s = workers.size();
// 记录历史最大工作线程数
if (s > largestPoolSize)
largestPoolSize = s;
// 将工作线程添加标识设置为true
workerAdded = true;
}
} finally {
// 释放锁
mainLock.unlock();
}
// 如果工作线程添加成功
if (workerAdded) {
// 启动工作线程(干活吧你!)
t.start();
// 将工作线程添加标识设置为true
workerStarted = true;
}
}
} finally {
// 如果作线程添加失败
if (! workerStarted)
//
addWorkerFailed(w);
}
return workerStarted;
}
/**
* 回滚工作线程创建.
* - 从workers中移除worker(如果存在)
* - 递减worker计数
* - 重新检查终止,以防该worker的存在阻碍终止
*/
private void addWorkerFailed(Worker w) {
// 加锁
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 如果worker存在
if (w != null)
// 从workers中移除worker
workers.remove(w);
// 将工作线程数-1(映射+1:compareAndIncrementWorkerCount(c))
decrementWorkerCount();
// 尝试将线程池状态变成TIDYING
tryTerminate();
} finally {
// 释放锁
mainLock.unlock();
}
}
}
7、Worker
public class ThreadPoolExecutor extends AbstractExecutorService {
/**
* Class Worker主要维护运行任务的线程的中断控制状态,以及其他次要的簿记。
* 此类机会性地扩展了AbstractQueuedSynchronizer,以简化获取和释放围绕
* 每个任务执行的锁。这可以防止旨在唤醒等待任务的工作线程而不是中断正在运行
* 的任务的中断。我们实现了一个简单的非可重入互斥锁,而不是使用重入锁,因为
* 我们不希望工作任务在调用setCorePoolSize等池控制方法时能够重新获取锁。
* 此外,为了抑制中断直到线程实际开始运行任务,我们将锁状态初始化为负值,并
* 在启动时将其清除(在runWorker中).
*/
private final class Worker
extends AbstractQueuedSynchronizer // 线程中断
implements Runnable
{
/** 工作线程的Thread对象,并且是在初始化时构建的. */
final Thread thread;
/** 需要执行的任务. */
Runnable firstTask;
/** Per-thread task counter */
volatile long completedTasks;
// 构造
Worker(Runnable firstTask) {
// AQS初始化,state=-1,刚刚初始化的线程是不能被中断的
setState(-1);
// 实例化的时候初始化任务
this.firstTask = firstTask;
// 通过线程工程给Worker构建Thread对象
this.thread = getThreadFactory().newThread(this);
}
/** t.start() */
public void run() {
runWorker(this);
}
// Lock methods
//
// 值0表示解锁状态.
// 值1表示锁定状态.
// (注:中断线程不是让线程立即停止,只是将thread的中断标识置为true)
protected boolean isHeldExclusively() {
return getState() != 0;
}
protected boolean tryAcquire(int unused) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
// 将 state 置为0,在 runworker中,为了表示当前线程允许被中断
protected boolean tryRelease(int unused) {
setExclusiveOwnerThread(null);
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) {
}
}
}
}
}
8、runWorker
执行任务的流程,并作了中断线程相关的 lock操作
public class ThreadPoolExecutor extends AbstractExecutorService {
/**
* 主工作程序运行循环。重复从队列中获取任务并执行它们,同时处理许多问题:
*
* 1. 我们可以从初始任务开始,在这种情况下,我们不需要获得第一个任务。否则,
* 只要池在运行,我们就从getTask获取任务。如果返回null,则工作进程将由于池
* 状态或配置参数的更改而退出。其他退出是由外部代码中的异常抛出导致的,在这种
* 情况下,completedAbruptly 成立,这通常会导致processWorkerExit替换
* 此线程。
*
* 2. 在运行任何任务之前,获取锁以防止任务执行时其他池中断,然后我们确保除非
* 池停止,否则该线程不会设置其中断。
*
* 3. 每个任务运行之前都会调用beforeExecute,这可能会引发异常,在这种情况
* 下,我们会导致线程在不处理任务的情况下死亡(CompletedThrough=true时
* 中断循环)
*
* 4. 假设beforeExecute正常完成,我们运行任务,收集任何抛出的异常以发送给
* afterExecute。我们分别处理RuntimeException、Error(这两个规范都保证
* 我们可以捕获)和任意抛出。因为我们不能在Runnable中重新播放垃圾。运行时,
* 我们将它们包装在输出的错误中(到线程的UncaughtExceptionHandler)。任
* 何抛出的异常也会保守地导致线程死亡。
*
* 5. 任务完成后。运行完成后,我们调用afterExecute,这也可能引发异常,这也会
* 导致线程死亡。根据JLS第14.20节的规定,即使task.run()抛出异常,此异常也是
* 有效的。
*
* 异常机制的净效果是,afterExecute和线程的UncaughtExceptionHandler
* 具有尽可能准确的信息,可以提供用户代码遇到的任何问题。
*
* @param w the worker
*/
final void runWorker(Worker w) {
// 拿到当前工作线程
Thread wt = Thread.currentThread();
// 拿到Worker对象中封装的任务
Runnable task = w.firstTask;
// 将Worker对象中firstTask归位
w.firstTask = null;
// worker.unlock()
w.unlock(); // 允许中断
// 任务执行时,钩子函数中是否抛出异常的标识!!!默认true
boolean completedAbruptly = true;
try {
// ⑴ 任务不为null(执行execute、submit时传入的任务)
while (task != null ||
// ⑵ 从工作队列获取任务不为null(从工作队列拿任务执行)
(task = getTask()) != null) {
// work.lock() :在SHUTDOWN状态下当前线程不允许被中断
// 非可重入锁,因此在中断时,也需要对worker进行lock,不能获取就代表当前工作线程正在执行任务
w.lock();
// 如果线程池状态是STOP,必须将当前线程终端
// 第一个判断:当前线程池状态是否>STOP
// 第二个判断:判断中断标记位并归位
// (false说明不是STOP,true则需要再次查看是否是并发操作导致线程池状态为STOP)
if ((runStateAtLeast(ctl.get(), STOP) || (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP))) &&
// 查询当前线程中断标记是否为false
!wt.isInterrupted())
// 将中断标记设置为true
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
task = null;
// 执行任务成功的个数+1
w.completedTasks++;
// worker.unlock() : 将state设置为0
w.unlock();
}
}
// 任务执行时,钩子函数中未抛出异常此标识置false
completedAbruptly = false;
} finally {
// 收尾工作
processWorkerExit(w, completedAbruptly);
}
}
}
9、getTask
从工作队列 workQueue获取任务
public class ThreadPoolExecutor extends AbstractExecutorService {
/**
* 如果为false(默认),则即使在空闲时,核心线程也保持活动状态。
* 如果为true,则核心线程使用 keepAliveTime 超时等待工作
*/
private volatile boolean allowCoreThreadTimeOut; // 如果设置true不如使用cacheThread
/**
* 根据当前配置设置对任务执行阻塞或定时等待,如果此工作进程由于以下任何原因而必须退出,则返回null:
*
* 1. 存在多于maximumPoolSize个工作线程(由于调用了setMaximumPoolSize)
* 2. 线程池已经停止
* 3. 线程池已关闭,队列为空
* 4. 此工作线程在等待任务时超时,超时工作线程在超时等待之前和之后都会终止(即,
* {@code allowCoreThreadTimeOut || workerCount > corePoolSize})
* 如果队列为非空,则此工作线程不是池中的最后一个线程。
*
* @return task, or null 如果worker必须退出,在这种情况下,workerCount将递减
*/
private Runnable getTask() {
// 超时标志(超时后非核心线程要被干掉):上次 poll() 超时了吗?
boolean timedOut = false;
for (;;) {
//========================1、判断线程池状态=====================
// 获取ctl
int c = ctl.get();
// 获取线程池状态
int rs = runStateOf(c);
// 仅在必要时检查队列是否为空(状态>=SHUTDOWN && <STOP)
// 干掉当前worker条件:
// ⑴ 线程池状态 >= STOP (STOP:不接收新任务,中断工作线程,不处理队列任务)
// ⑵ rs >= SHUTDOWN && workQueue.isEmpty() (SHUTDOWN:不接收新任务,可处理队列任务)
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
// 工作线程个数-1
decrementWorkerCount();
// 返回null,在后续processWorkerExit方法干掉当前worker
return null;
}
//========================2、判断工作线程数量=====================
// 获取当前工作线程数量
int wc = workerCountOf(c);
// workers 是否会被淘汰? 允许超时或者大于核心线程数(是否是核心线程只看数量不看线程)
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
// 是否超过最大线程数 || workers可淘汰且超时
if ((wc > maximumPoolSize || (timed && timedOut))
// 满足上面条件后,工作线程数量 > 1 或工作队列为空,就干掉当前worker
&& (wc > 1 || workQueue.isEmpty())) {
// 通过CAS方式移除当前线程
if (compareAndDecrementWorkerCount(c))
// 返回null,在后续processWorkerExit方法干掉当前worker
return null;
continue; // 如果多线程必定只有一个成功,其他线程继续循环重新尝试干掉他们各自的worker
}
//=======================3、尝试从工作队列拿任务=====================
try {
Runnable r = timed ?
// (非核心线程)阻塞一定时间从工作线程拿任务,如果超时非核心线程就要被干掉了
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
// (核心线程)一直阻塞,一直等着拿任务继续干活
workQueue.take();
if (r != null)
return r; // 拿到任务返回上一级方法开启线程干活
// 非核心线程超时了也没拿到任务,超时标志置true,继续循环尝试返回null干掉当前worker
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false; // workQueue.take()抛出的异常,继续循环尝试重新获取任务
}
}
}
}
10、processWorkerExit
public class ThreadPoolExecutor extends AbstractExecutorService {
/**
* 已完成任务的计数器。仅在终止worker线程时更新。仅在主锁下访问。
*/
private long completedTaskCount;
/**
* 为垂死的worker执行清理和记账。
* 仅从工作线程调用。除非设置了CompletedStrutly,否则假设workerCount已被调整以考虑退出。
* 此方法从工作线程集中删除线程,如果由于用户任务异常而退出,或者如果运行的工作线程少于
* corePoolSize,或者队列非空但没有工作线程,则可能终止池或替换工作线程。
*
* @param w the worker
* @param completedAbruptly 如果工作人员因用户异常而死亡
*/
private void processWorkerExit(Worker w, boolean completedAbruptly) {
// 如果执行此方法不是getTask返回null导致的,而是直接由于异常导致的(一般是那俩增强钩子函数抛的)
if (completedAbruptly) // 如果是突然的,则workerCount没有调整
decrementWorkerCount(); // 手动工作线程数-1
// 加锁!
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 将当前worker执行任务的总数汇总到计数器
completedTaskCount += w.completedTasks;
// 干掉当前worker
workers.remove(w);
} finally {
// 释放锁
mainLock.unlock();
}
// 尝试将线程池状态变成TIDYING
tryTerminate();
// 获取ctl
int c = ctl.get();
// 如果线程池状态<STOP:RUNNING或SHUTDOWN
// (只有<STOP才往下判断,因为STOP就不处理工作队列的任务了,就算当前正在执行的任务也要尝试中断)
if (runStateLessThan(c, STOP)) {
// 如果是正常状态移除当前worker
if (!completedAbruptly) {
// 获取可存活的核心线程数
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
// 如果min为0 并且 工作队列不为空
if (min == 0 && ! workQueue.isEmpty())
// min设置为1
min = 1;
// 如果当前工作线程数 >= min:有worker在干活且数量不少于min
if (workerCountOf(c) >= min)
return; // 直接return,当前worker删了就删了,无所谓
}
// 走到这里说明是非正常状态移除了worker 或者 当前工作线程数 < min
// 再添加一个工作线程做任务干活
addWorker(null, false);
}
}
/**
* 如果(SHUTDOWN and pool and queue empty)或(STOP and pool empty)过渡到 TERMINATED 状态
* 如果符合终止条件,但workerCount为非零,则中断空闲工作进程以确保关机信号传播。
* 必须在任何可能导致终止的操作之后调用此方法——减少worker数量或在关闭期间从队列中删除任务。
* 该方法是非私有的,允许从ScheduledThreadPoolExecutor进行访问。
*/
final void tryTerminate() {
for (;;) {
int c = ctl.get();
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 {
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
}
}
}