了解下JUC的线程池学习四(ThreadPoolExecutor#execute执行过程)
1.execute方法源码分析
线程池异步执行任务的方法实现是ThreadPoolExecutor#execute(),源码如下:
// 执行命令,其中命令(下面称任务)对象是Runnable的实例
public void execute(Runnable command) {
// 判断命令(任务)对象非空
if (command == null)
throw new NullPointerException();
// 获取ctl的值
int c = ctl.get();
// 判断如果当前工作线程数小于核心线程数,则创建新的核心线程并且执行传入的任务
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
// 如果创建新的核心线程成功则直接返回
return;
// 这里说明创建核心线程失败,需要更新ctl的临时变量c
c = ctl.get();
}
// 走到这里说明创建新的核心线程失败,也就是当前工作线程数大于等于corePoolSize
// 判断线程池是否处于运行中状态,同时尝试用非阻塞方法向任务队列放入任务(放入任务失败返回false)
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
// 这里是向任务队列投放任务成功,对线程池的运行中状态做二次检查
// 如果线程池二次检查状态是非运行中状态,则从任务队列移除当前的任务调用拒绝策略处理之(也就是移除前面成功入队的任务实例)
if (! isRunning(recheck) && remove(command))
// 调用拒绝策略处理任务 - 返回
reject(command);
// 走到下面的else if分支,说明有以下的前提:
// 0、待执行的任务已经成功加入任务队列
// 1、线程池可能是RUNNING状态
// 2、传入的任务可能从任务队列中移除失败(移除失败的唯一可能就是任务已经被执行了)
// 如果当前工作线程数量为0,则创建一个非核心线程并且传入的任务对象为null - 返回
// 也就是创建的非核心线程不会马上运行,而是等待获取任务队列的任务去执行
// 如果前工作线程数量不为0,原来应该是最后的else分支,但是可以什么也不做,
//因为任务已经成功入队列,总会有合适的时机分配其他空闲线程去执行它
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 走到这里说明有以下的前提:
// 0、线程池中的工作线程总数已经大于等于corePoolSize(简单来说就是核心线程已经全部懒创建完毕)
// 1、线程池可能不是RUNNING状态
// 2、线程池可能是RUNNING状态同时任务队列已经满了
// 如果向任务队列投放任务失败,则会尝试创建非核心线程传入任务执行
// 创建非核心线程失败,此时需要拒绝执行任务
else if (!addWorker(command, false))
// 调用拒绝策略处理任务 - 返回
reject(command);
}
流程执行:
第一步:如果当前工作线程总数小于corePoolSize,则直接创建核心线程执行任务(任务实例会传入直接用于构造工作线程实例)。
第二步:如果当前工作线程总数大于等于corePoolSize,判断线程池是否处于运行中状态,
同时尝试用非阻塞方法向任务队列放入任务,这里会二次检查线程池运行状态,
如果当前工作线程数量为0,则创建一个非核心线程并且传入的任务对象为null。
第三步:如果向任务队列投放任务失败(任务队列已经满了),则会尝试创建非核心线程传入任务实例执行。
第四步:如果创建非核心线程失败,此时需要拒绝执行任务,调用拒绝策略处理任务。
注:为什么需要二次检查线程池的运行状态,当前工作线程数量为0,
尝试创建一个非核心线程并且传入的任务对象为null?这个可以看API注释:
如果一个任务成功加入任务队列,我们依然需要二次检查是否需要添加一个工作线程
(因为所有存活的工作线程有可能在最后一次检查之后已经终结)
或者执行当前方法的时候线程池是否已经shutdown了。所以我们需要二次检查线程池的状态,
必须时把任务从任务队列中移除或者在没有可用的工作线程的前提下新建一个工作线程。
任务提交流程从调用者的角度来看如下:

学习来源:https://www.cnblogs.com/throwable/p/13574306.html

浙公网安备 33010602011771号