线程池核心线程是如何保持住的?
概述
本文尝试回答以下几个问题: 1、核心线程池是如何保持住的? 2、当没有任务时,超过核心线程数的线程是如何回收的? 3、线程队列为什么必须是BlockingQueue,普通队列行不行?
背景知识
以下是一些背景知识,方便后面分析问题,如果明白可以跳过,否则建议重温下。
线程参数
corePoolSize:核心线程数。
maximumPoolSize:最大线程数。
keepAliveTime:空闲线程存活时间。
TimeUnit:时间单位。
BlockingQueue:线程池任务队列。
ThreadFactory:创建线程的工厂。
RejectedExecutionHandler:拒绝策略,包括Abort、callRuns,discard,discardOld.
线程池结构
以ThreadPoolExecutor为例,它的结构如下所示: 最重要的两个类就是我们的ThreadPoolExecutor和Worker,其中ThreadPoolExecutor就是线程池本池了,Worker是对Thread的一个简单封装,它实现了Runnable接口,我们可以把它当作线程Thread本身。我们需要知道这些字段的含义是怎样的:
提交任务做了什么?
创建线程池没有任何有价值的东西,就是对ThreadPoolExecutor的属性进行赋值,例如我们刚才提到的那几个重要的属性,所以我们从execute提交任务开始探索内部。 :
public void execute(Runnable command) {
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
// 当线程数量小于核心线程数时,创建新的线程,请注意这里给了command,也就是我们自定义的Runnable任务,后面会再次提到它!
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
// 上面没有走到,说明核心线程数量已经满了,则尝试将任务添加到任务队列中,也就是这里的workQueue
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false)) // 添加队列失败,说明队列满了。尝试依赖maxPoolSize创建线程,如果失败则根据拒绝策略处理任务
reject(command);
}
是不清晰多了?不过我们需要继续深挖,看看addWorker()干了什么?第二个boolean参数又是干嘛用的?
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
// for循环主要通过cas更新线程worker数量
int c = ctl.get(); // 拿到线程数量
for (;;) {
int wc = workerCountOf(c);
if (wc >= CAPACITY ||
wc >=