看源码https://segmentfault.com/a/1190000044699924
当核心线程满了时,先放阻塞队列,阻塞队列满了才去申请非核心线程!
public void execute(Runnable command){
if(command = nul1)
throw new NullPointerException();
/*...*/
int c= ctl.get();
if(workerCountOf(c) < corePoolSize){
// 当前活跃线程数小于核心线程数,则创建核心线程
if(addWorker(command, true))
return; // 创建成功
c=ctl.get();
}
// 创建失败,核心线程满了,尝试放入阻塞队列
if(isRunning(c) && workQueue.offer(command)){
int recheck=ctl.get();
// 再次确认:判断当前线程池状态是否为running(运行中)
if(!isRunning(recheck) && remove(command)) // 如果不是running状态,需要将任务从核心队列中清除
reject(command); // 并执行拒绝策略
else if(workerCountOf(recheck)==0)
// 构建空任务的非核心线程(core=false)
addWorker(null, false);
// 放入阻塞队列失败,阻塞队列满了,尝试创建非核心线程执行任务
}else if(!addWorker(command,false))
// 非核心线程创建失败,可能是因为队列已满或线程池配置不允许放入更多任务,执行拒绝策略。
reject(command);
}
在一个新任务进来时,如果核心线程数设置为0或者设置核心线程的存活时间导致超时被销毁,线程池内无核心线程可用(因为阻塞队列没满,所以也没有非核心线程)但是新任务被放到了阻塞队列,这种情况会导致任务一直在队列中放着(任务会饥饿),所以需要添加非任务的空核心线程避免任务饥饿。
为什么不直接addWorker(command, false)而是addWorker(null, false)?
因为线程池的设计理念之一是任务一旦成功放入阻塞队列,应该依赖线程池中的线程从队列中提取任务并执行,而不是在入队后直接将任务交给新创建的线程。
所以此时任务已经在阻塞队列里面了,不要直接把任务交给线程,而是让一个空闲线程从队列里面获取任务来执行。
(通过调用 addWorker(null, false),新创建的线程会自动从队列中获取任务,这符合线程池的工作流程,保持了线程池操作的简单和一致性。)