添加任务到线程池
- 判断任务是否为空, 任务为空,结束。
- 任务不为空,判断能否
创建核心线程
,若能创建,结束
能否创建核心线程
((ctl的29位,设置的corepoolsoze 共同判断)
创建核心线程会存在失败的情况,假设设置核心线程数位5,已经创建了4个,还剩1个,此时A和B都来创建,只有一个能创建成功。 - 若核心线程没有创建成功,或者已经到达设置的核心线程数的值了,则判断线程池状态是否正常,若
线程池状态是Running状态
,就可以把任务添加到阻塞任务
。若成功把任务扔到阻塞队列中
,判断当前工作线程是否是0个
,若是
,则添加一个非核心
工作线程处理,添加成功,结束。若不是,结束
。
判断当前工作线程是否是0个,记住,核心线程数是可以设置为0个的。
添加到阻塞任务也会存在失败情况,比如只剩最后一个位置,2个线程同时添加,就会存在失败的情况。
当把任务扔到阻塞队列中,一定要保证有工作线程处理它。 - 若添加工作线程失败,当在
阻塞任务中扔任务
后发现,线程池状态不是Running状态
了,则执行拒绝策略
,执行完毕,结束。
存在在阻塞任务中扔任务前,线程池状态是Running状态
阻塞任务中扔任务后,线程池状态不是Running状态。 当阻塞队列放满了
,线程池状态是Running状态,尝试添加非核心工作线程
,若成功添加,结束。若失败,则执行拒绝策略,结束。
添加非核心工作线程,(ctl的29位, 设置的max 最大线程数 共同判断)
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);
}