一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情。
线程池是我们最常用的并发组件之一。关于线程池的核心参数:核心线程数 corePoolSize,最大线程数 maximumPoolSize,活跃时间 keepAliveTime,阻塞队列 blockingQueue、拒绝策略处理器 rejectedExecutionHandler,就不再介绍了。这篇文章主要是从源码角度分析线程池。
线程池工作流程
线程池工作主要流程:
关键源码解析
核心控制状态 ctl
其为一个原子整型类AtomicInteger,即记录了线程池状态,又记录了工作线程数,它是如何做到的呢?
虽然它只有「1」个整型,但是它同时是「32」位整型,线程池将状态记录在高位中,将工作线程数记录在低位中。 然后通过与(&)、或(|)操作快速得到ctl值,线程池状态,工作线程数
// 运行状态
private static final int RUNNING = -1 << COUNT_BITS; // 11100000000000000000000000000000
// 线程池状态和工作线程数或操作(高低位或操作)得到ctl
// 111(高3位) ....(低29位) —— 线程状态
// 000(高3位) ..11(低29位) —— 工作线程数
private static int ctlOf(int rs, int wc) { return rs | wc; }
// 获得线程池状态,通过与操作实现
// 111(高3位) ....(低29位) —— ctl
// 111(高3位) ..00(低29位全为0) —— ~CAPACITY
// 结果,只留下了最高3位,低位29位为0,其即为线程池状态
private static int runStateOf(int c) { return c & ~CAPACITY; }
// 获得工作线程数,通过与操作实现
// 111(高3位) ....(低29位) —— ctl
// 000(高3位) ..11(低29位全为1) —— CAPACITY
// 结果,只留下了低位29位,高位3位为0,其即为工作线程数据
private static int workerCountOf(int c) { return c & CAPACITY; }
核心业务逻辑
- 工作线程数小于核心线程数,新增一个工作线程数
- 核心线程数已满,将任务加入工作队列
- 加入队列未成功(队列已满),新增非核心工作线程。
- 最大线程数也添加失败(工作线程数大于最大线程数),交由拒绝策略处理任务
具体对应源码如下:
#. public void execute(Runnable command)方法下内容
int c = ctl.get();
// 1. 工作线程数小于核心线程数,新增一个工作线程数
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
// 2. 核心线程数已满,将任务加入工作队列
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 3.加入队列未成功(队列已满),新增非核心工作线程。
else if (!addWorker(command, false))
// 4. 最大线程数也添加失败(工作线程数大于最大线程数),交由拒绝策略处理任务
reject(command);
内部工作类 Worker
当任务提交到线程池时,不是直接创建一个Thread对象来运行任务,而是创建一个内部类Worker对象,再运行线程。
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable{
// 方法:private boolean addWorker(Runnable firstTask, boolean core)
// 方法内部代码段
Worker w = null;
w = new Worker(firstTask);
工作队列的任务执行
当线程池核心线程数满了的时候,新进来的任务先放入工作队列中。线程池执行的线程会一直轮询获取工作队列的任务。如果队列为空,线程会阻塞住(如果已超核心线程数或者配值了allowCoreThreadTimeOut,则会阻塞一段时间后返回空对象),直至新的任务加入。
//runWorker方法内
while (task != null || (task = getTask()) != null){
...
}
// getTask方法内
// 判断是否限时
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
// 读取队列数据
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();