1. 前言
我发现很多人对线程池的一个执行流程都有一个误解,很多人理解的线程池的执行流程是这样的
其实中间有一个有大的漏洞,很多人会觉得,只有当阻塞对列满的时候,才会去添加非核心工作线程,包括很多文章或者视频中,都是讲的错的,当我们往阻塞队列添加任务的时候,他并不会傻傻的等待,而是会判断线程池中的一个工作线程,如果工作线程数为零,为了防止线程饥饿,会创建一个非核心工作线程,下面是线程池的execute方法的注释
2. 线程的池的execute方法
// 提交任务到线程池的核心方法
// 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. 即便有核心线程,可以设置核心线程也允许超时,设置allowCoreThreadTimeOut为true,代表核心线程也可以超时
else if (workerCountOf(recheck) == 0)
// 为了避免阻塞队列中的任务饥饿,添加一个非核心工作线程去处理
addWorker(null, false);
}
// 任务添加到阻塞队列失败
// 构建一个非核心工作线程
// 如果添加非核心工作线程成功,直接完事,告辞
else if (!addWorker(command, false))
// 添加失败,执行决绝策略
reject(command);
}