ThreadPoolExecutor
的源码解析
这篇自己肝了很久的一篇文章,里面主要介绍关于对
ThreadPoolExecutor
源码的绝对详细教程在你看到此篇,我希望大家最好跟着将源码的注释自己敲一遍,绝对事半功倍!!
祝大家收获满满,哈哈!
本文总纲
文章目录
线程池的核心属性
ctl:当前的
ctl
就是一个int
类型的数值,内部是基于AtomicInteger
套了一层,进行运算时,是原子性的
ctl
表示的线程池的两种核心状态:
- 线程池的状态:
ctl
的高3位标识线程池的状态- 工作线程的状态:
ctl
的低29位,表示工作线程的个数
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
COUNT_BITS: 声明了一个常量,
COUNT_BITS = 29
Integer.SIZE
:在获取Integer
的bit的个数
private static final int COUNT_BITS = Integer.SIZE - 3;
CAPACITY:就是当前工作线程能记录工作线程的最大个数
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
线程池五种状态的表示
1.RUNNING:
只有此状态表示线程池没有问题**,可以正常接受任务处理**
111**:高三位(代表running状态),
running
表示可以处理任务,或者阻塞队列的任务
private static final int RUNNING = -1 << COUNT_BITS;
2.SHUTDOWN:
000:代表
shutdown
的状态,不会接受新任务,正在处理的任务正常进行,阻塞队列的任务也会完成
private static final int SHUTDOWN = 0 << COUNT_BITS;
3.STOP:
001:代表我们的stop状态,不会接受新任务,正在处理任务的线程会被中断,阻塞队列的任务一个不管
private static final int STOP = 1 << COUNT_BITS;
4.TIDYING:
010:代表TIDYING状态,这个状态是
shutdown
或者stop
的状态转换过来的,代表当前线程马上关闭,就是一个过渡状态
private static final int TIDYING = 2 << COUNT_BITS;
5.TERMINATED
011:代表TERMINATED状态,这个状态是TIDYING状态转换过来的转换过来只需要执行一个terminated方法
private static final int TERMINATED = 3 << COUNT_BITS;
线程池的核心属性整体源码解析代码
//当前的ctl就是一个int类型的数值,内部是基于AtomicInteger套了一层,进行运算时,是原子性的
//ctl表示的线程池的两种核心状态:
//线程池的状态: ctl的高3位标识线程池的状态
//工作线程的状态:ctl的低29位,表示工作线程的个数
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
//声明了一个常量, COUNT_BITS = 29
//Integer.SIZE:在获取Integer的bit的个数
private static final int COUNT_BITS = Integer.SIZE - 3;
//CAPACITY就是当前工作线程能记录工作线程的最大个数
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// 线程池状态的表示:
//当前的五个状态:只有running状态表示线程池没有问题,可以正常接受任务处理
///111:高三位(代表running状态),running表示可以处理任务,或者阻塞队列的任务
private static final int RUNNING = -1 << COUNT_BITS;
///000:代表shutdown的状态,不会接受新任务,正在处理的任务正常进行,阻塞队列的任务也会完成
private static final int SHUTDOWN = 0 << COUNT_BITS;
//001:代表我们的stop状态,不会接受新任务,正在处理任务的线程会被中断,阻塞队列的任务一个不管
private static final int STOP = 1 << COUNT_BITS;
//010:代表TIDYING状态,这个状态是shutdown或者stop的状态转换过来的,代表当前线程马上关闭,就是一个过渡状态
private static final int TIDYING = 2 << COUNT_BITS;
//011:代表TERMINATED状态,这个状态是TIDYING状态转换过来的转换过来只需要执行一个terminated方法
private static final int TERMINATED = 3 << COUNT_BITS;
// Packing and unpacking ctl:在使用下面的三个值时,需要传进来ctl
//拿到高三位的值
//基于&运算的特点,保证只会拿到ctl的高三位值
private static int runStateOf(int c) { return c & ~CAPACITY; }
//基于&运算的特点,保证只会拿到ctl的低29位的值
private static int workerCountOf(int c) { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
线程池的状态转换图
ThreadPoolExecutor的有参构造
//无论调用哪个有参构造,都会执行当前的有参构造
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
//健壮性校验
//核心线程个数允许为0,最大线程数必须大于0,最大线程数要大于等于核心线程数
//非核心线程的最大空闲时间,可以等于0
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
//不满足要求就抛出参数异常
throw new IllegalArgumentException();
//阻塞队列,线程工厂,拒绝策略都不允许为null,为null就抛就抛空指针异常
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
//此行为系统资源访问策略,和线程池核心业务关系不大
this.acc = System.getSecurityManager() == null ? null : AccessController.getContext();
//各种赋值,在JUC包下,几乎所有涉及到线程挂起的操作,单位都是用纳秒
//有参构造的值,都赋值给我们成员变量
//doug lea习惯就是将成员变量作为局部变量单独操作
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
ThreadPoolExecutor的execute
方法
execute
是提交任务到线程池的核心方法,很重要
这里我强烈大家跟着敲一遍注释,绝对收获受益匪浅
线程池的执行流程其实就是再说execute方法内部作了那些判断
源码解析:
execute()
:是提交任务到线程池的核心方法
参数:command
就是提交过来的任务
public void execute(Runnable command) {
提交的任务不能为null,否则抛空指针异常
if (command == null) throw new NullPointerException();
ctl.get()
:获取核心属性ctl
,用于后面的判断
int c = ctl.get();
解释: 如果工作线程个数小于核心线程数,满足要求,添加核心工作线程
workerCountOf(c)
:查询工作线程个数
corePoolSize
:核心线程数
addworker
(任务,是核心线程吗): 是否添加工作线程
if (workerCountOf(c) < corePoolSize) {
//addworker(任务,是核心线程吗)
//addworker返回true,代表添加工作线程成功
//addworker返回false,代表添加工作线程失败
//addWorker会给予线程池状态以及工作线程个数做判断,查看能否添加工作线程
if (addWorker(command, true))
//工作线程构建出来了,任务也交给command去处理了
return;
//说明线程池状态或者是工作线程个数发生了变化,导致添加失败,重新获取一次ctl
c = ctl.get();
}
添加核心线程失败,走下面的步骤(上述的步骤都失败,走下面的)
isRunning(c)
:判断线程池状态是否是running
workQueue.offer(command)
:基于阻塞队列的offer
方法,将任务添加到阻塞队列在此阻塞队列添加收否成成功又有两种情况:
- 成功添加
- 重新获取ctl
- 判断线程池的状态是否为
running
:
- 否:任务从阻塞队列移除,并直接执行拒绝策略
- 是: 继续下一步
- 查看工作线程数是否是0个
- 是:添加一个非核心线程处理
- 否: 结束本次流程
//添加核心线程失败,走下面的步骤
//判断线程池状态是否是running,如果是,正常基于阻塞队列的offer方法,将任务添加到阻塞队列
if (isRunning(c) && workQueue.offer(command)) {
//如果任务添加到阻塞队列成功,直接走if内部
//如果任务在扔到阻塞队列之前线程池的状态改变了,
//重新获取ctl
int recheck = ctl.get();
//如果线程池的状态不是running,将任务从阻塞队列移除,并直接执行拒绝策略
if (! isRunning(recheck) && remove(command))
reject(command);
//阻塞队列有我刚刚放进去的任务
//查看工作线程数是否是0个
//如果工作线程为0个,需要添加一个非核心工作线程去处理阻塞队列中的任务
//发生这种情况由两种:
//1.构建线程池时,核心线程为0个
//2.即便有核心线程,也可以设置核心线程允许超时,设置allowsCoreThreadTimeOut等于true,表示核心线程可以超时
else if (workerCountOf(recheck) == 0)
//为了避免阻塞队列的任务饥饿,添加一个非核心线程处理
addWorker(null, false);
}
上述条件都不符合,直接直接执行决绝策略,结束
//任务添加到阻塞队列失败
//构建一个非核心工作线程
//如果添加非核心线程成功,直接结束
else if (!addWorker(command, false))
//添加 失败,执行拒绝策略
reject(command);
完整源码解析
//提交任务到线程池的核心方法
//command就是提交过来的任务
public void execute(Runnable command) {
//提交的任务不能为null
if (command == null)
throw new NullPointerException();
//获取核心属性ctl,用于后面的判断
int c = ctl.get();
//如果工作线程个数小于核心线程数
//满足要求,添加核心工作线程
if (workerCountOf(c) < corePoolSize) {
//addworker(任务,是核心线程吗)
//addworker返回true,代表添加工作线程成功
//addworker返回false,代表添加工作线程失败
//addWorker会给予线程池状态以及工作线程个数做判断,查看能否添加工作线程
if (addWorker(command, true))
//工作线程构建出来了,任务也交给command去处理了
return;
//说明线程池状态或者是工作线程个数发生了变化,导致添加失败,重新获取一次ctl
c = ctl.get();
}
//添加核心线程失败,走下面的步骤
//判断线程池状态是否是running,如果是,正常基于阻塞队列的offer方法,将任务添加到阻塞队列
if (isRunning(c) && workQueue.offer(command)) {
//如果任务添加到阻塞队列成功,直接走if内部
//如果任务在扔到阻塞队列之前线程池的状态改变了,
//重新获取ctl
int recheck = ctl.get();
//如果线程池的状态不是running,将任务从阻塞队列移除,并直接执行拒绝策略
if (! isRunning(recheck) && remove(command))
reject(command);
//阻塞队列有我刚刚放进去的任务
//查看工作线程数是否是0个
//如果工作线程为0个,需要添加一个非核心工作线程去处理阻塞队列中的任务
//发生这种情况由两种:
//1.构建线程池时,核心线程为0个
//2.即便有核心线程,也可以设置核心线程允许超时,设置allowsCoreThreadTimeOut等于true,表示核心线程可以超时
else if (workerCountOf(recheck) == 0)
//为了避免阻塞队列的任务饥饿,添加一个非核心线程处理
addWorker(null, false);
}
//任务添加到阻塞队列失败
//构建一个非核心工作线程
//如果添加非核心线程成功,直接结束
else if (!addWorker(command, false))
//添加 失败,执行拒绝策略
reject(command);
}
ThreadPoolExecutor的execute方法解析完整流程图
注意,到这里并没有完结撒花哈!~
正在更新中,但是上面的execute()
执行流程图我强烈推荐大家跟着画一遍,很有收获的!!!
后面更新对addworker()
方法源码的剖析
4.ThreadPoolExecutor
的addworker()
方法
addWorker()
主要分成两大块去看:
第一块:校验线程池的状态以及工作线程的个数
源码解析
retry
: 是给我们的外层for循环添加一个标记,为了方便内层for循环跳出外层for循环使用的
runStateOf(c)
:拿到ctl
高三位的值
int rs = runStateOf(c);
线程池状态判断:
如果线程池状态为SHUTDOWN
,并且阻塞队列有任务,工作线程个数为0,添加一个工作线程去处理阻塞队列任务
判断线程池的状态是否大于等于SHUTDOWN
,若满足,说明线程池非running
状态
if (rs >= SHUTDOWN &&
//这三个条件都满足,代表要添加非核心工作线程去处理阻塞队列任务
//有一个没有满足,代表不需要添加,直接返回false
! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty()))
//代表不要添加工作线程
return false;
workerCountOf(c)
:基于ctl
拿到低29位的值,代表当前工作线程个数
int wc = workerCountOf(c);
wc >= CAPACITY
:工作线程个数大于最大值,则不能再添加
wc >= (core ? corePoolSize : maximumPoolSize)
:基于core
来判断是否核心,
- 是:基于
corePoolSize
判断- 否: 基于
maximumPoolSize
判断
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
//代表不能添加,工作线程不满足要求
return false;
compareAndIncrementWorkerCount(c)
:针对ctl
进行+1,采用CAS
方式
//针对ctl进行+1,采用CAS方式,
if (compareAndIncrementWorkerCount(c))
//CAS成功后,直接退出外层循环,代表可以直接执行添加工作线程操作
break retry;
重新获取一次
ctl
的值
c = ctl.get(); // Re-read ctl
runStateOf(c)
:判断重新获取到的ctl
中,表示的线程池状态跟之前的是否有区别
//若状态不一样,说明有变化,重新去判断线程池的状态
if (runStateOf(c) != rs)
//跳出一次外层for循环
continue retry;
添加工作线程之校验完整源码
private boolean addWorker(Runnable firstTask, boolean core) {
//外层for循环校验线程池的状态,内层for循环校验工作线程的个数
//retry是给我们的外层for循环添加一个标记,为了方便内层for循环跳出外层for循环使用的
retry:
for (;;) {
//获取ctl
int c = ctl.get();
//拿到ctl高三位的值
int rs = runStateOf(c);
//=====线程池状态判断==================
//如果线程池状态为SHUTDOWN,并且阻塞队列有任务,工作线程个数为0,添加一个工作线程去处理阻塞队列任务
// 判断线程池的状态是否大于等于SHUTDOWN,若满足,说明线程池非running状态,
if (rs >= SHUTDOWN &&
//这三个条件都满足,代表要添加非核心工作线程去处理阻塞队列任务
//有一个没有满足,代表不需要添加,直接返回false
! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty()))
//代表不要添加工作线程
return false;
for (;;) {
//======工作线程个数判断==========
//基于ctl拿到低29位的值,代表当前工作线程个数
int wc = workerCountOf(c);
//如果工作线程个数大于最大值,则不能再添加,并直接返回false
if (wc >= CAPACITY ||
//基于core来判断是否核心,是:基于corePoolSize判断 否: 基于maximumPoolSize判断
wc >= (core ? corePoolSize : maximumPoolSize))
//代表不能添加,工作线程不满足要求
return false;
//针对ctl进行+1,采用CAS方式,
if (compareAndIncrementWorkerCount(c))
//CAS成功后,直接退出外层循环,代表可以直接执行添加工作线程操作
break retry;
//重新获取一次ctl的值
c = ctl.get(); // Re-read ctl
//判断重新获取到的ctl中,表示的线程池状态跟之前的是否有区别
//若状态不一样,说明有变化,重新去判断线程池的状态
if (runStateOf(c) != rs)
//跳出一次外层for循环
continue retry;
}
}
//=====省略添加工作线程和启动工作线程过程(看下面)
}
第二块:添加工作线程并且启动工作线程
声明三个标识:
workerStarted
: 表示工作线程启动没(默认false)workerAdded
:工作线程添加没(默认false)Worker
: 代表工作线程(默认为null)
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
构建工作线程,并且将任务传递进去
w = new Worker(firstTask);
获取
Worker
中的Thread
对象
final Thread t = w.thread;
判断
Thread
对象是否不为null,在new Worker
时,内部会通过给予的ThreadFactory
去构建Thread交给Worker
if (t != null)
加锁,保证使用workers成员变量以及对
largestPoolSize
赋值时,保证线程安全
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
rs
:再次获取线程池状态
int rs = runStateOf(ctl.get());
rs < SHUTDOWN
:若满足rs < SHUTDOWN
,说明线程池时RUNNING状态,状态正常,执行if代码块
(rs == SHUTDOWN && firstTask == null)
:若线程池状态为SHUTDOWN,并且firstTask
为null,添加非核心工作线程处理阻塞队列任务
if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null))
t.isAlive()
:检验ThreadFactory构建线程后,不能自己启动线程,如果启动了,抛出异常
if (t.isAlive())
throw new IllegalThreadStateException();
将new好的worker添加到
HashSet
中当中的workers:就是HashSet集合
//private final HashSet<Worker> workers = new HashSet<Worker>();
workers.add(w);
获取HashSet的
size
,拿到工作线程个数
int s = workers.size();
largestPoolSize:在记录最大线程个数的记录
s > largestPoolSize
:如果当前工作线程个数,大于最大线程个数的记录,就赋值
if (s > largestPoolSize)
largestPoolSize = s;
添加工作线程成功
workerAdded = true;
addWorkerFailed()
: 工作线程启动失败,需要做的补偿操作
private void addWorkerFailed(Worker w) {
//因为操作workers,需要加锁
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//若w不为null,之前Worker已经new出来了
if (w != null)
//从HashSet中移除,
workers.remove(w);
//同时对ctl进行-1,代表去掉一个工作线程个数
decrementWorkerCount();
//因为工作线程启动失败,判断一下状态的问题,是不是走TIDYING状态,最终到TERMINATED状态,
tryTerminate();
} finally {
//
mainLock.unlock();
}
}
第二块完整源码
private boolean addWorker(Runnable firstTask, boolean core) {
//========省略校验线程池的状态以及工作线程的个数代码(看上面的)
//添加工作线程并且启动工作线程
//声明了三个标识:
//workerStarted: 工作线程启动没(默认false)
boolean workerStarted = false;
//workerAdded:工作线程添加没(默认false)
boolean workerAdded = false;
//Worker: 代表工作线程(默认为null)
Worker w = null;
try {
//构建工作线程,并且将任务传递进去
w = new Worker(firstTask);
//获取Worker中的Thread对象
final Thread t = w.thread;
//判断Thread对象是否不为null,在new Worker时,内部会通过给予的ThreadFactory去构建Thread交给Worker
//若t=null,代表ThreadFactory有问题
if (t != null) {
//加锁,保证使用workers成员变量以及对largestPoolSize赋值时,保证线程安全
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 再次获取线程池状态
int rs = runStateOf(ctl.get());
//再次判断
//若满足rs < SHUTDOWN,说明线程池时RUNNING状态,状态正常,执行if代码块
if (rs < SHUTDOWN ||
//若线程池状态为SHUTDOWN,并且firstTask为null,添加非核心工作线程处理阻塞队列任务
(rs == SHUTDOWN && firstTask == null)) {
//到这,可以添加工作线程
if (t.isAlive()) // t.isAlive():检验ThreadFactory构建线程后,不能自己启动线程,如果启动了,抛出异常
throw new IllegalThreadStateException();
//private final HashSet<Worker> workers = new HashSet<Worker>();
//将new好的worker添加到HashSet中
workers.add(w);
//获取HashSet的size,拿到工作线程个数
int s = workers.size();
//largestPoolSize在记录最大线程个数的记录
//如果当前工作线程个数,大于最大线程个数的记录,就赋值
if (s > largestPoolSize)
largestPoolSize = s;
//添加工作线程成功
workerAdded = true;
}
} finally {
mainLock.unlock();
}
//若添加工作线程成功,
if (workerAdded) {
//直接启动worker中线程
t.start();
//启动工作线程成功
workerStarted = true;
}
}
} finally {
//作补偿的操作,如果工作线程启动失败,将这个添加失败的工作线程处理掉
//
if (! workerStarted)
addWorkerFailed(w);
}
//返回工作线程启动成功
return workerStarted;
}
//工作线程启动失败,需要做的补偿操作
private void addWorkerFailed(Worker w) {
//因为操作workers,需要加锁
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//若w不为null,之前Worker已经new出来了
if (w != null)
//从HashSet中移除,
workers.remove(w);
//同时对ctl进行-1,代表去掉一个工作线程个数
decrementWorkerCount();
//因为工作线程启动失败,判断一下状态的问题,是不是走TIDYING状态,最终到TERMINATED状态,
tryTerminate();
} finally {
//
mainLock.unlock();
}
}
5.ThreadPoolExecutor
的worker()
方法
worker
对象
主要包含了两个内容
- 工作线程要执行任务
- 工作线程可能会被中断,控制中断
Worker管理任务
thread
:线程工厂构建的线程
final Thread thread;
firstTask
:当前worker要执行的任务
Runnable firstTask;
completedTasks
:记录当前工作线程处理了多少个任务
volatile long completedTasks;
Worker
的有参构造
- 状态设置
- 任务赋值
- 构建Thread
Worker(Runnable firstTask) {
//将state设置为-1,代表当前不允许中断线程
setState(-1); // inhibit interrupts until runWorker
//任务赋值
this.firstTask = firstTask;
//基于线程工作构建Thread,并且传入的Runnable是Worker
this.thread = getThreadFactory().newThread(this);
}
run()
:调用runWorker
进行任务的执行
public void run() {
//任务执行时,执行的是runWorker方法
runWorker(this);
}
Worker管理中断
这块其实没有什么好说的,主要我们需要看里面的一个方法,是关键
interruptIfStarted()
:是在中断工作线程时,执行的方法
//当前方法是在中断工作线程时,执行的方法
void interruptIfStarted() {
Thread t;
//只有Worker中state>=0时,可以中断工作线程
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
//如果状态正常,并且线程未中断,这边就中断线程
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
worker()
完整源码解析附注释
//worker继承了AQS,目的就是为控制工作线程的中断
//worker实现了Runnable,内部的Thread对象,在执行start时,必然会执行worker中断的一些操作
private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
private static final long serialVersionUID = 6138294804551838833L;
//==============Worker管理任务==========
//线程工厂构建的线程
final Thread thread;
//当前worker要执行的任务
Runnable firstTask;
//记录当前工作线程处理了多少个任务
volatile long completedTasks;
//有参构造
Worker(Runnable firstTask) {
//将state设置为-1,代表当前不允许中断线程
setState(-1); // inhibit interrupts until runWorker
//任务赋值
this.firstTask = firstTask;
//基于线程工作构建Thread,并且传入的Runnable是Worker
this.thread = getThreadFactory().newThread(this);
}
//当Thread执行start方法时,调用的是Worker的run方法
public void run() {
//任务执行时,执行的是runWorker方法
runWorker(this);
}
//==============Worker管理中断==========
protected boolean isHeldExclusively() {
return getState() != 0;
}
protected boolean tryAcquire(int unused) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
//设置state为0
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;
//只有Worker中state>=0时,可以中断工作线程
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
//如果状态正常,并且线程未中断,这边就中断线程
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
}
6.ThreadPoolExecutor
的runWorker()
方法
runWorker
方法:就是让工作线程拿到任务去执行即可
源码剖析:
wt
:拿到当前线程
Thread wt = Thread.currentThread();
task
:从Worker对象中拿到任务
Runnable task = w.firstTask;
将
Worker
中的firstTask
置为空
w.firstTask = null;
将Worker中的
state
置为0,代表当前线程可以中断
w.unlock();
completedAbruptly
: 判断工作线程是否是异常结束,默认就是异常结束
boolean completedAbruptly = true;
获取任务:
1.
task != null
:直接拿到第一个任务区执行
2.task = getTask()) != null
如若第一个任务为null,去阻塞队列中那任务
while (task != null || (task = getTask()) != null)
执行Worker的Lock方法,当前在Lock时,shutdown操作不能中断当前线程,因为当前线程正在处理任务
w.lock();
1.
(runStateAtLeast(ctl.get(), STOP) && !wt.isInterrupted()
:线程池到STOP状态,并且当前线程还没有中断,确保线程是中断的,进到if内部执行中断方法2.
(Thread.interrupted() && runStateAtLeast(ctl.get(), STOP)))&& !wt.isInterrupted()
:如果线程池状态非STOP状态,确保线程不是中断的,若发现中断标记位为true了,再次查看线程池状态是大于STOP了–>再次中断线程
if ((runStateAtLeast(ctl.get(), STOP) ||(Thread.interrupted() && runStateAtLeast(ctl.get(), STOP)))
&& !wt.isInterrupted())
钩子函数:在线程池中没有做任何的操作,若需要再线程池任务执行前后做一些任务处理,需要自己重写实现钩子函数
beforeExecute()
:前置钩子函数(点进源码会发现啥都没有,需要自己实现)
beforeExecute(wt, task);
执行任务
task.run();
后置钩子函数
afterExecute(task, thrown);
完整源码解析
//工作线程启动后执行的任务
final void runWorker(Worker w) {
//拿到当前线程
Thread wt = Thread.currentThread();
//从Worker对象中拿到任务
Runnable task = w.firstTask;
//将Worker中的firstTask置为空
w.firstTask = null;
//将Worker中的state置为0,代表当前线程可以中断
w.unlock(); // allow interrupts
//判断工作线程是否是异常结束,默认就是异常结束
boolean completedAbruptly = true;
try {
//获取任务:
//1.直接拿到第一个任务区执行
//2.如若第一个任务为null,去阻塞队列中那任务
while (task != null || (task = getTask()) != null) {
//执行Worker的Lock方法,当前在Lock时,shutdown操作不能中断当前线程,因为当前线程正在处理任务
w.lock();
// 比较ctl>=STOP ,如果满足这个状态,说明线程池已经到了STOP状态,甚至已经要结束,
//也就是:若线程池状态>=STOP,确保线程中断
//1.(runStateAtLeast(ctl.get(), STOP) && !wt.isInterrupted() 线程池到STOP状态,并且当前线程还没有中断,确保线程是中断的,进到if内部执行中断方法
//2.如果线程池状态非STOP状态,确保线程不是中断的,若发现中断标记位为true了,再次查看线程池状态是大于STOP了-->再次中断线程
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;
//当前工作线程处理的任务书+1
w.completedTasks++;
//执行unlock方法,此时shutdown方法才可以中断当前线程
w.unlock();
}
}
//若while循环结束走到这,说明正常结束,在getTask会做一个额外的处理,将ctl-1,代表工作线程减少一个
completedAbruptly = false;
} finally {
//考虑干掉工作线程(下面专门说了)
processWorkerExit(w, completedAbruptly);
}
}
介绍
processWorkerExit()
工作线程结束前,要执行的此方法
且在内部也处理了工作线程正常和异常结束时的处理方案
private void processWorkerExit(Worker w, boolean completedAbruptly) {
//如果是异常结束
if (completedAbruptly)
//将ctl-1,扣掉一个工作线程
decrementWorkerCount();
//操作Worker,为了线程安全,加锁
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//当前工作线程处理的任务个数累加到线程池处理的任务个数属性中(就是做一个总的统计)
completedTaskCount += w.completedTasks;
//将工作线程从hashset中移除
workers.remove(w);
} finally {
//释放锁
mainLock.unlock();
}
//只要工作线程凉了,查看是不是线程池状态改变
tryTerminate();
//获取ctl
int c = ctl.get();
//判断线程池状态:要么是RUNNING,要么是SHUTDOWN
if (runStateLessThan(c, STOP)) {
//如果正常结束工作线程,
if (!completedAbruptly) {
//如果核心线程允许超时,min=0,否则就是核心线程个数
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
//若为min==0,可能会出现没有工作线程,并且阻塞队列有任务,没人处理
if (min == 0 && ! workQueue.isEmpty())
//至少有一个工作线程处理阻塞队列任务
min = 1;
//如果工作线程个数大于等于1,不怕没线程处理,正常return
if (workerCountOf(c) >= min)
return; // replacement not needed
}
//异常结束,为避免出问题,添加一个空任务的非核心线程来填补上刚刚结束的工作线程
addWorker(null, false);
}
}
7.ThreadPoolExecutor
的getTask()
说明:工作线程在去获取阻塞队列任务前,首先要查看线程池的状态,若状态没有问题,进行阻塞队列的take或者poll任务;对于第二个循环,不仅要判断线程池的状态,还要判断当前工作线程是否可以被干掉
当前方法就是在阻塞队列中获取任务
- 前半部分是判断当前工作线程是否是可以返回null结束
- 后半部分就是从阻塞队列中拿任务的
源码解析
timeout
:默认值是false
boolean timedOut = false;
死循环
for (;;)
第一次循环
拿到
ctl
int c = ctl.get();
拿到线程池的状态
int rs = runStateOf(c);
rs >= SHUTDOWN
:若线程池状态是STOP,不需在处理阻塞队列的任务,直接返回null
rs >= STOP || workQueue.isEmpty())
:若线程池状态是SHUTDOWN,并且阻塞队列是空,直接返回null
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
//若可以返回null,先扣减工作线程个数
decrementWorkerCount();
//返回null,结束runWorker的while循环
return null;
}
wc
:基于ctl
拿到工作线程个数
int wc = workerCountOf(c);
第二次循环
wc > maximumPoolSize
:若工作线程个数大于最大线程数(一般情况不会满足,即通常为false)
timed && timedOut
:
- 代表工作线程**<=**核心线程数,必为false;
- 即便工作线程个数>核心线程个数,此时第一次循环也不会为true,因为
timeOut
默认值是false- 为什么会有? 考虑第二次循环,因循环内部必有修改
timeOut
的位置
wc > 1 || workQueue.isEmpty())
:要么工作线程还有,要么阻塞队列为空并且满足上述条件后,工作线程才会走到if内部,结束工作线程
if ((wc > maximumPoolSize || (timed && timedOut))
//要么工作线程还有,要么阻塞队列为空并且满足上述条件后,工作线程才会走到if内部,结束工作线程
&& (wc > 1 || workQueue.isEmpty())) {
//第二次循环才有可能到这
//正常结束:工作线程减一,因为是CAS操作,若失败,重新走for循环
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
工作线程从阻塞队列拿任务
若是核心线程,timed为false,若是非核心线程,timed就是true
- 若是非核心,走poll方法,拿任务等待一会
- 若是核心走take,死等
Runnable r = timed ?
//若是非核心,走poll方法,拿任务等待一会
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
//若是核心走take,死等
workQueue.take();
完整源码剖析
private Runnable getTask() {
timeout默认值是false
boolean timedOut = false;
//死循环
for (;;) {
//========第一次
//拿到ctl
int c = ctl.get();
//拿到线程池的状态
int rs = runStateOf(c);
//若线程池状态是STOP,不需在处理阻塞队列的任务,直接返回null
//若线程池状态是SHUTDOWN,并且阻塞队列是空,直接返回null
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
//若可以返回null,先扣减工作线程个数
decrementWorkerCount();
//返回null,结束runWorker的while循环
return null;
}
//基于ctl拿到工作线程个数
int wc = workerCountOf(c);
// 核心线程允许超时,timed为true
//or 工作线程个数大于核心线程个数 timed为true
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
//========第二次循环=======================
if (//wc > maximumPoolSize:若工作线程个数大于最大线程数(一般情况不会满足,即通常为false)
//timed && timedOut :代表工作线程<=核心线程数,必为false; 即便工作线程个数>核心线程个数,此时第一次循环也不会为true,因为timeOut默认值是false
//考虑第二次循环,因循环内部必有修改timeOut的位置
(wc > maximumPoolSize || (timed && timedOut))
//要么工作线程还有,要么阻塞队列为空并且满足上述条件后,工作线程才会走到if内部,结束工作线程
&& (wc > 1 || workQueue.isEmpty())) {
//第二次循环才有可能到这
//正常结束:工作线程减一,因为是CAS操作,若失败,重新走for循环
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
//工作线程从阻塞队列拿任务
try {
//若是核心线程,timed为false,若是非核心线程,timed就是true
Runnable r = timed ?
//若是非核心,走poll方法,拿任务等待一会
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
//若是核心走take,死等
workQueue.take();
//从阻塞队列拿到的任务不为null,就正常返回任务,去执行
if (r != null)
return r;
//说明当前线程没拿到任务将timeOut设置为true在上面就可以返回null退出
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
8.ThreadPoolExecutor
的关闭方法
ShutdownNow()
首先我们需要查看ShutdownNow()
方法,可以让线程池状态从RUNNING
直接转为STOP
状态
完整源码,重点看里面的方法
//shutdownNow方法不会阻塞队列的任务,将任务全部返回
public List<Runnable> shutdownNow() {
//声明返回结果
List<Runnable> tasks;
//加锁
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//关于系统访问决策的,在此次不关注,不作解释
checkShutdownAccess();
//将线程池状态改为STOP
advanceRunState(STOP);
//无论怎样,直接中断工作线程
interruptWorkers();
//将阻塞队列的任务全部扔到list集合中
tasks = drainQueue();
} finally {
//释放锁
mainLock.unlock();
}
tryTerminate();
return tasks;
}
advanceRunState()
:
将线程池状态改为STOP
private void advanceRunState(int STOP) {
//死循环
for (;;) {
//获取ctl
int c = ctl.get();
//runStateAtLeast(c, STOP):当前线程池状态>=STOP,不管了
//ctl.compareAndSet(c, ctlOf(STOP, workerCountOf(c))):
// 基于CAS,将ctl从c修改为STOP状态,不修改工作线程个数,但是状态改为了STOP
// 若修改成功,结束
if (runStateAtLeast(c, STOP) ||
ctl.compareAndSet(c, ctlOf(STOP, workerCountOf(c))))
break;
}
}
interruptWorkers
:
直接中断工作线程
private void interruptWorkers() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//遍历HASHset,拿到所有的工作线程,直接中断
for (Worker w : workers)
w.interruptIfStarted();
} finally {
mainLock.unlock();
}
}
drainQueue
:
将阻塞队列的任务全部移除扔到list集合中
private List<Runnable> drainQueue() {
BlockingQueue<Runnable> q = workQueue;
ArrayList<Runnable> taskList = new ArrayList<Runnable>();
//阻塞队列自带,直接清空阻塞队列内容扔到list集合
q.drainTo(taskList);
//为了避免任务丢失,重新判断,是否需要重新编辑阻塞队列,重新扔到list集合
if (!q.isEmpty()) {
for (Runnable r : q.toArray(new Runnable[0])) {
if (q.remove(r))
taskList.add(r);
}
}
return taskList;
}
tryTerminate
:
查看当前线程池是否可以变为TERMINATED状态
final void tryTerminate() {
//死循环
for (;;) {
//拿到ctl
int c = ctl.get();
//若是RUNNING,直接return
//若状态>=TIDYING,要结束了,直接return
//若状态=SHUTDOWN,但阻塞队列还是任务,直接return
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return;
//若还有工作线程,
if (workerCountOf(c) != 0) { // Eligible to terminate
//再次中断工作线程
interruptIdleWorkers(ONLY_ONE);
//return,就是等工作线程全结束,再次尝试就如到TERMINATED的状态
return;
}
//加锁,为了执行Condition的释放操作
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//将线程池状态修改为TIDYING,若成功,往下执行
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
//这个方法为空,若需要在线程池关闭后做一些额外操作,自行继承实现
terminated();
} finally {
//最终修改为TERMINATED状态
ctl.set(ctlOf(TERMINATED, 0));
//线程池提供了一个方法,主线程提交任务到线程池后可以继续作其他操作
//也可以让主线程提交任务后,等待线程池处理完毕,再做后续的操作
//线程池凉凉后,要唤醒那些调用了awaitTermination方法的线程
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
// else retry on failed CAS
}
}
Shutdown()
方法
再次我们需要查看Shutdown()
方法,可以从RUNNING
直接转为SHUTDOWN
状态
SHUTDOWN
:在shutdown
状态下,不会中断正在干活的线程,而且会处理阻塞队列的任务
public void shutdown() {
//加锁
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//系统的调用策略,不看
checkShutdownAccess();
//一个死循环方法,将线程池状态修改为shutdown状态
advanceRunState(SHUTDOWN);
//中断空闲线程
interruptIdleWorkers();
//为了ScheduledThreadPoolExecutor的线程作准备的,不管
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
//尝试结束线程
tryTerminate();
}
interruptIdleWorkers
:中断空闲线程
private void interruptIdleWorkers(boolean onlyOne) {
//加锁
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers) {
Thread t = w.thread;
//若线程没有中断,那么就去获取worker的锁,基于trylock可知,不会中断正在干活的线程
if (!t.isInterrupted() && w.tryLock()) {
try {
//会中断空闲线程
t.interrupt();
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
if (onlyOne)
break;
}
} finally {
mainLock.unlock();
}
}
好了,此篇总结下吧
后面我会持续进行更新此篇,直到整个ThreadPoolExecutor
源码分析完为止(不拆成几篇博客了**,方便以后**学习查看)
哈哈哈,已经都完成了哦,很详细了,我肝了两三个晚上的结果,希望对你有帮助
感觉不错,点个赞呗,祝你好运加倍!~
道阻且长,行则将至!诸君共勉之