常用并发组件源码分析——线程池

75 阅读1分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情

线程池是我们最常用的并发组件之一。关于线程池的核心参数:核心线程数 corePoolSize,最大线程数 maximumPoolSize,活跃时间 keepAliveTime,阻塞队列 blockingQueue、拒绝策略处理器 rejectedExecutionHandler,就不再介绍了。这篇文章主要是从源码角度分析线程池。

线程池工作流程

线程池工作主要流程:

线程池工作流程.png

关键源码解析

核心控制状态 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; }
核心业务逻辑
  1. 工作线程数小于核心线程数,新增一个工作线程数
  2. 核心线程数已满,将任务加入工作队列
  3. 加入队列未成功(队列已满),新增非核心工作线程。
  4. 最大线程数也添加失败(工作线程数大于最大线程数),交由拒绝策略处理任务

具体对应源码如下:

#. 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();
并发控制,保证线程安全