线程池核心方法源码 (附带流程图)
公众号搜索: 意姆斯Talk, 即可领取大量学习资料及实战经验
温馨提醒: 若图片模糊看不清, 右键图片, 点击在新标签中打开图片
前戏链接(主要是线程池中的概念, 概念决定一切)
https://blog.csdn.net/qq_37805943/article/details/114748473
1.execute(Runnable command)
command:待执行的参数
1.1源码
public void execute(Runnable command) {
//1.判断当前任务是否为空, 若为空, 则抛出空指针异常
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
* one. 如果运行的线程少于corePoolSize,请尝试以给定的命令作为第一个线程开始一个新线程
* 任务。调用addWorker原子地检查运行状态和workerCount,因此可以防止错误警报
* 如果不应该,则会添加线程,返回false
*
* two. 如果任务可以成功排队,那么我们仍然需要仔细检查是否应该添加线程,(因为自从上次检查以来已有的已经死了)或者自进入此方法后,池已关闭。
* 所以我们重新检查状态,必要时回滚排队已停止,如果没有线程,则启动新线程
*
* three. 如果无法将任务排队,则尝试加新的线程。如果失败了,我们就知道我们已经关闭或者饱和了
* 所以拒绝这个任务。
*/
//2.获取到当前线程数
int c = ctl.get();
//2.1若当前线程数小于核心线程数, 调用addWorker()方法添加任务, 若添加成功,直接返回
if (workerCountOf(c) < corePoolSize) {
//command:当前入参, ture:核心线程数, false:最大线程数
if (addWorker(command, true))
return;
//2.2添加失败, 获取当前线程数, 走阻塞队列
c = ctl.get();
}
//3.若处于Running状态, 同时将当前任务添加队列成功
if (isRunning(c) && workQueue.offer(command)) {
//3.1二次校验
int recheck = ctl.get();
//3.2若处于非Running状态&&从消息队列中成功移除当前任务
if (! isRunning(recheck) && remove(command))
//调用拒绝策略方法
reject(command);
//3.3若当前线程数等于0
else if (workerCountOf(recheck) == 0)
//3.4添加一个null任务,这个线程将去获取已经加入任务队列的本次任务并执行
addWorker(null, false);
}
//4.尝试扩容maxPoolSize后再次addWorker,失败则拒绝任务
else if (!addWorker(command, false))
reject(command);
}
1.2流程图
![在这里插入图片描述](https://img-blog.csdnimg.cn/202103301758513.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM3ODA1OTQz,size_16,color_FFFFFF,t_70#pic_center
2.addWorker(Runnable firstTask, boolean core)
Runnable firstTask:首先执行的任务(可以为null)
boolean core: true表示核心线程数, false表示最大线程数
2.1源码
private boolean addWorker(Runnable firstTask, boolean core) {
//retry用法:用于两层for循环, 跳转到第一个for循环下
//两个for循环的目的就是为了通过CAS算法将核心线程数量+1, 跳出整个循环
retry:
for (;;) {
//1.获取当前线程数
int c = ctl.get();
//2.获取当前线程池的状态
int rs = runStateOf(c);
/*
*3.仅在必要时检查队列是否为空,
*条件1: 当线程池状态>=shutdown(意思就是非running状态)
*条件2: 不满足(线程池状态为shutdown, 同时firstTask为空,并且队列不为空)
*两者都满足时, 返回false, 结束当前方法, 说明添加任务失败
*/
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
/*
*4.wc:获取当前线程数
* 条件1:当wc大于最大上限容量
* 条件2:wc大于等于核心线程数或最大线程数
* 返回false, 添加失败,东西太多了还加个锤子!
*/
int wc = workerCountOf(c);
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
//5.若成功通过CAS算法+1, 则跳出整个循环
if (compareAndIncrementWorkerCount(c))
break retry;
//6.再次获取当前线程数
c = ctl.get(); // Re-read ctl
//7.如果当前状态不等于running状态,则结束本次循环,继续for循环下去
if (runStateOf(c) != rs)
continue retry;
}
}
//7.开始和结束默认是false(小case:不赋值boolean默认就是false)
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
w = new Worker(firstTask);
//7.1其中运行的线程
final Thread t = w.thread;
if (t != null) {
//7.2 其实就是加锁 ReentrantLock mainLock = new ReentrantLock();
final ReentrantLock mainLock = this.mainLock;
//7.3 开启锁(俗称:保安模式)
mainLock.lock();
try {
//7.3.1获取到当前线程数, 同时获取线程池的状态
int rs = runStateOf(ctl.get());
/*
*条件1: 若当前rs线程状态是running状态
*条件2: 若当前线程状态是shutdown状态,并且首先执行的firstTask为空
*两者有一个为真, 则if(true).走if判断
*/
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
//7.3.1.1若当前线程存在(已经启动了),则抛出非法线程状态异常
if (t.isAlive())
throw new IllegalThreadStateException();
//7.3.1.2加入线程池
workers.add(w);
int s = workers.size();
//7.3.1.2若线程池中的数量大于上一次的最大线程数,则赋值,仅在mainLock中访问
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
//7.3.2释放锁
mainLock.unlock();
}
//7.3.3若加入线程池成功,则启动线程--->这就是为什么上面抛非法线程状态异常
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
//7.4 若线程启动失败, workerStarted=false,则调用addWorkerFailed()方法
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
2.2流程图
3.Worker(上述2中定义的Worker类)
Worker这个工作线程,实现了Runnable接口,并持有一个线程thread,一个初始化的任务firstTask。thread是在调用构造方法时通过ThreadFactory来创建的线程,可以用来执行任务;firstTask用它来保存传入的第一个任务,这个任务可以有也可以为null。如果这个值是非空的,那么线程就会在启动初期立即执行这个任务,也就对应核心线程创建时的情况;如果这个值是null,那么就需要创建一个线程去执行任务列表(workQueue)中的任务,也就是非核心线程的创建。
Worker类主要维护正在运行任务的线程的中断控制状态,以及其他次要的记录。这个类适时地继承了AbstractQueuedSynchronizer类,以简化获取和释放锁(该锁作用于每个任务执行代码)的过程。这样可以防止去中断正在运行中的任务,只会中断在等待从任务队列中获取任务的线程。
3.1源码
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
/**
* 此类永远不会序列化, 但是我们提供了serialVersionUID来禁止Javac警告。
*/
private static final long serialVersionUID = 6138294804551838833L;
/** 此工作线程正在运行的线程。如果工厂失败,则为null. */
final Thread thread;
/** 最开始的任务, 可以为空. */
Runnable firstTask;
/** 线程任务计数器 */
volatile long completedTasks;
/**
* 使用给定的第一个任务和ThreadFactory中的线程创建。
* firstTask: 第一个任务(如果没有则为null)
*/
// 通过构造函数初始化,
Worker(Runnable firstTask) {
//设置AQS的同步状态:,锁状态,-1为初始值,0为unlock状态,1为lock状态
setState(-1); // inhibit interrupts until runWorker 在调用runWorker前,禁止中断
this.firstTask = firstTask;
// 线程工厂创建一个线程
this.thread = getThreadFactory().newThread(this);
}
/*
*将主运行循环委托给外部runWorker()
*/
public void run() {
//调用了runWorker方法来执行任务, 主要是调用runWorker里面的getTask()方法
runWorker(this); //runWorker()是ThreadPoolExecutor的方法
}
/*
* state: 锁状态,-1为初始值,0为unlock状态,1为lock状态
*/
protected boolean isHeldExclusively() {
return getState() != 0;
}
/**
* tryAcquire:英文翻译尝试获取, 尝试获取锁的方法
* 重写AQS的tryAcquire(),AQS本来就是让子类来实现的
*/
protected boolean tryAcquire(int unused) {
// 判断原值为0,且重置为1,所以state为-1时,锁无法获取。
// 每次都是0->1,保证了锁的不可重入性
if (compareAndSetState(0, 1)) {
// 设置exclusiveOwnerThread=当前线程
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
/**
* 尝试释放锁
* 不是state-1,而是置为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(); }
/**
* 中断(如果运行)
* shutdownNow时会循环对worker线程执行
* 且不需要获取worker锁,即使在worker运行时也可以中断
*/
void interruptIfStarted() {
Thread t;
//如果state>=0、t!=null、且t没有被中断
//new Worker()时state==-1,说明不能中断
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
}
3.2Worker类的执行模型
3.3特点
Worker是通过继承AQS,使用AQS来实现独占锁这个功能。没有使用可重入锁ReentrantLock,而是使用AQS,为的就是实现不可重入的特性去反应线程现在的执行状态。
lock方法一旦获取了独占锁,表示当前线程正在执行任务中。
如果正在执行任务,则不应该中断线程。
如果该线程现在不是独占锁的状态,也就是空闲的状态,说明它没有在处理任务,这时可以对该线程进行中断。
线程池在执行shutdown方法或tryTerminate方法时会调用interruptIdleWorkers方法来中断空闲的线程,interruptIdleWorkers方法会使用tryLock方法来判断线程池中的线程是否是空闲状态;如果线程是空闲状态则可以安全回收。
4.runWorker(Worker worker)核心源码
worker: 封装的Worker,携带了工作线程的诸多要素,包括Runnable(待处理任务)、lock(锁)、completedTasks(记录线程池已完成任务数)
4.1源码
final void runWorker(Worker w) {
//1.获取当前的线程
Thread wt = Thread.currentThread();
//2.获取worker中封装的参数
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock();
//3.线程退出的原因,true是任务导致,false是线程正常退出
boolean completedAbruptly = true;
try {
// 当前任务和从任务队列中获取的任务都为空,方停止循环, 详情看4.1.1中的getTask()源码解析
while (task != null || (task = getTask()) != null) {
w.lock();
/**
* 条件1:线程池状态>=STOP,即STOP或TERMINATED
* 条件2:若线程已经被中断,再次检查线程池状态是否>=STOP
* 条件3: wt未中断时
* 条件1与条件2任意满意一个,并且wt不是中断状态,则中断wt,否则进入下一步
*/
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
//3.1执行前
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 {
//3.2执行后
afterExecute(task, thrown);
}
} finally {
task = null;
//3.3worker中的参数(线程任务计数器)completedTasks+1
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
//4.处理worker的退出
processWorkerExit(w, completedAbruptly);
}
}
4.1.1getTask()源码
作用: 在任务队列(workQueue)中获取 task(Runnable)。
简化流程:
- while循环不断地通过getTask()方法获取任务。
- getTask()方法从阻塞队列中取任务。
- 如果线程池正在停止,那么要保证当前线程是中断状态,否则要保证当前线程不是中断状态。
- 执行任务。
- 如果getTask结果为null则跳出循环,执行processWorkerExit()方法,销毁线程。
判断里可参考如图
private Runnable getTask() {
//1.设置超时变量默认为false
boolean timedOut = false; // Did the last poll() time out?
//2.进入循环
for (;;) {
//2.1获取当前线程数和线程池的状态
int c = ctl.get();
int rs = runStateOf(c);
/*
*条件1:当前状态处于非running状态
*条件2:线程池STOP、TIDYING、TERMINATED状态或workQueue为空
*两者都满足时, 进入if判断
*/
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
//2.1线程池不必再获取任务了,当前工作线程数量-1并返回null
decrementWorkerCount();
return null;
}
//获取当前线程数量
int wc = workerCountOf(c);
/*条件1:allowCore:如果为false,为处于空闲状态,核心线程也保持活动状态。
* 如果为true,则核心线程使用keepAliveTime来超时等待工作。
*条件2:若当前线程数量大于核心线程数,则为ture,反之为false
*两者满足其一,则timed=true
*timed:阻塞时间是否限制, ture受限,false不受限
*/
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
/* 条件1:工作线程数大于maximumPoolSize,或(timed为true,同时timeOut是否超时)
* 条件2:wc > 1或任务队列为空(||或的意义:一true全true)
*两者满足其一:则通过CAS使工作线程量减1,若成功减1,返回null
*/
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
//执行到这里,说明已经经过前面重重校验,开始真正获取task了
try {
// 如果工作线程阻塞时间受限,则使用poll(),否则使用take()
// poll()设定阻塞时间,而take()无时间限制,直到拿到结果为止
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
//r不为空,则返回该Runnable
if (r != null)
return r;
//若runnable为null,则将超时时间设置为true,
timedOut = true;
} catch (InterruptedException retry) {
//响应中断,进入下一次循环前将最近获取任务超时状态置为false
timedOut = false;
}
}
}
4.1.2流程图
5.processWorkerExit(Worker w, boolean completedAbruptly)
Worker w: 要结束的工作线程。
boolean completedAbruptly : 是否突然完成(异常导致),如果工作线程因为用户异常死亡,则completedAbruptly参数为 true。
private void processWorkerExit(Worker w, boolean completedAbruptly) {
//1.若工作线程因为出问题抛异常, 则为true,通过CAS算法使工作线程数-1
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
decrementWorkerCount();
//2.其实就是加锁 ReentrantLock mainLock = new ReentrantLock(), 开启锁
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
/* 2.1
* completedTaskCount =completedTaskCount + w.completedTasks;
* completedTaskCount 计数器完成的任务, 只有在mainLock下才能用
* 将该worker已完成的任务数追加到线程池已完成的任务数
*/
completedTaskCount += w.completedTasks;
//HashSet<Worker> workers中移除要结束的工作线程
workers.remove(w);
} finally {
//2.2解锁
mainLock.unlock();
}
//3 根据线程池状态进行判断是否结束线程池, 这个方法就是用来尝试终止线程池的
tryTerminate();
//4获取当前线程数
int c = ctl.get();
/**
* 5.是否需要增加工作线程
* 线程池状态是running 或 shutdown
* 如果当前线程是突然终止的,addWorker()
* 如果当前线程不是突然终止的,但当前线程数量 < 要维护的线程数量,addWorker()
* 故如果调用线程池shutdown(),直到workQueue为空前,线程池都会维持corePoolSize个线程,然后再逐渐销毁这corePoolSize个线程
*/
if (runStateLessThan(c, STOP)) {
if (!completedAbruptly) {
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
if (min == 0 && ! workQueue.isEmpty())
min = 1;
if (workerCountOf(c) >= min)
return; // replacement not needed
}
addWorker(null, false);
}
}
6.参考资料
[1]JDK 1.8 源码
[2] 维基百科-线程池
[3] 更好的使用Java线程池
[5] 深入理解Java线程池:ThreadPoolExecutor
[6]观看博主对线程池的看法有感: 云深i不知处
7.作者: 往下爬的小刘
end!