Java 线程池(ThreadPoolExecutor)源码分析

Java 线程池(ThreadPoolExecutor)源码分析

一、整体结构

scss 复制代码
ThreadPoolExecutor
├── 核心参数:corePoolSize、maximumPoolSize、workQueue、keepAliveTime、threadFactory、handler
├── 状态:ctl (AtomicInteger,高3位状态 + 低29位线程数)
├── 工作线程:Worker(继承 AQS,实现 Runnable)
└── 任务队列:BlockingQueue<Runnable>

二、execute():提交任务入口

java 复制代码
public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    
    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);
        return;
    }
    // 步骤3:队列满,尝试创建非核心线程
    if (!addWorker(command, false))
        reject(command);  // 步骤4:创建失败,执行拒绝策略
}

步骤说明

步骤 逻辑 说明 原因
1 workerCountOf(c) < corePoolSize 当前工作线程数小于核心线程数 核心线程是常驻的,优先用核心线程执行,减少线程创建开销
1.1 addWorker(command, true) 创建核心线程并执行该任务 核心线程不足时立即创建,firstTask 直接交给新线程执行,无需入队
2 workQueue.offer(command) 线程数已满,尝试把任务放入队列 核心线程已满时先入队等待,队列有界时可控制背压,避免无限制创建线程
2.1 isRunning(recheck) 再次检查池是否还在运行,防止并发下状态变化 入队与取任务存在并发,可能在此期间调用了 shutdown,需 double-check 避免向已关闭池提交
2.2 workerCountOf(recheck) == 0 若没有工作线程,补一个空任务 Worker,保证队列任务能被消费 可能所有 Worker 已退出,但队列还有任务,需补 Worker 否则任务永远无法执行
3 addWorker(command, false) 队列已满,尝试创建非核心线程执行任务 队列满说明负载高,临时扩容到 maximumPoolSize 以应对峰值
4 reject(command) 线程数已达 maximumPoolSize,执行拒绝策略 池已满,按 RejectedExecutionHandler 策略处理(抛异常、丢弃、调用者执行等)

三、addWorker():添加工作线程

java 复制代码
private boolean addWorker(Runnable firstTask, boolean core) {
    retry:
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);
        // 步骤1:检查线程池状态
        if (rs >= SHUTDOWN && !(rs == SHUTDOWN && firstTask == null && !workQueue.isEmpty()))
            return false;
        for (;;) {
            int wc = workerCountOf(c);
            // 步骤2:检查线程数是否超限
            if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize))
                return false;
            if (compareAndIncrementWorkerCount(c))
                break retry;
            c = ctl.get();
            if (runStateOf(c) != rs)
                continue retry;
        }
    }
    // 步骤3:创建 Worker 并加入 workers 集合
    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
        w = new Worker(firstTask);
        final Thread t = w.thread;
        if (t != null) {
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                int rs = runStateOf(ctl.get());
                if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) {
                    if (t.isAlive())
                        throw new IllegalThreadStateException();
                    workers.add(w);
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            if (workerAdded) {
                t.start();  // 步骤4:启动线程
                workerStarted = true;
            }
        }
    } finally {
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}

步骤说明

步骤 逻辑 说明 原因
1 rs >= SHUTDOWN 判断 若已 SHUTDOWN/STOP/TIDYING/TERMINATED,一般不再接受新任务 关闭后不应再创建新 Worker,避免资源浪费和状态混乱
1.1 特殊:SHUTDOWN && firstTask==null && !workQueue.isEmpty() 允许添加「空任务」Worker 去消费队列剩余任务 shutdown 后不再接新任务,但需把队列中已有任务执行完,空 Worker 会通过 getTask 取队列任务
2 wc >= corePoolSizemaximumPoolSize 核心/非核心线程数已达上限,不再创建 防止超过配置的线程数,保证资源可控
2.1 compareAndIncrementWorkerCount(c) CAS 增加工作线程计数,避免并发超限 多线程并发 execute 时,CAS 保证 workerCount 准确,防止超卖
3 new Worker(firstTask)workers.add(w) 创建 Worker,加入 workers 集合 Worker 封装线程与任务,workers 用于 shutdown 时遍历中断
4 t.start() 启动 Worker 内部线程,进入 runWorker() 线程启动后进入循环取任务、执行任务的流程

四、Worker:工作线程封装

java 复制代码
private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
    final Thread thread;      // 实际执行任务的线程
    Runnable firstTask;       // 创建时分配的第一个任务(可为 null)
    
    Worker(Runnable firstTask) {
        setState(-1);  // 初始状态,防止被中断
        this.firstTask = firstTask;
        this.thread = getThreadFactory().newThread(this);  // 把自己作为 Runnable
    }
    
    public void run() {
        runWorker(this);  // 委托给 runWorker
    }
}

步骤说明

步骤 逻辑 说明 原因
1 setState(-1) 初始 AQS 状态为 -1 防止 Worker 刚创建时被误判为可中断,runWorker 中 unlock 后才允许中断
2 getThreadFactory().newThread(this) 用线程工厂创建线程,this 作为 Runnable 统一线程创建方式,Worker 的 run 会调用 runWorker,形成执行入口
3 继承 AQS 实现简单锁 区分空闲(lock 前)与工作中(lock 后),shutdown 时只中断空闲 Worker,避免打断正在执行的任务

五、runWorker():工作线程主循环

java 复制代码
final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock();  // 允许中断
    boolean completedAbruptly = true;
    try {
        // 步骤1:循环取任务(firstTask 或 getTask())
        while (task != null || (task = getTask()) != null) {
            w.lock();
            // 步骤2:检查是否需要中断(shutdown 等)
            if ((runStateAtLeast(ctl.get(), STOP) || 
                 (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP))) &&
                !wt.isInterrupted())
                wt.interrupt();
            try {
                beforeExecute(wt, task);  // 钩子
                try {
                    task.run();  // 步骤3:执行任务
                } finally {
                    afterExecute(task, thrown);  // 钩子
                }
            } finally {
                task = null;
                w.completedTasks++;
                w.unlock();
            }
        }
        completedAbruptly = false;
    } finally {
        processWorkerExit(w, completedAbruptly);  // 步骤4:Worker 退出处理
    }
}

步骤说明

步骤 逻辑 说明 原因
1 task = w.firstTaskgetTask() 优先执行创建时传入的 firstTask,之后从队列取任务 firstTask 是 addWorker 时传入的,直接执行可少一次入队出队;之后从队列取任务实现复用
2 状态检查 + 中断 若池已 STOP 或当前线程被中断,确保线程被中断,以便快速退出 STOP 时要尽快结束,中断可让阻塞在 getTask 的线程及时退出
3 task.run() 直接执行任务(不是 start(),复用当前线程) Worker 线程就是工作线程,直接 run 在当前线程执行,无需再起新线程
4 processWorkerExit() 线程退出时:减少 worker 计数、必要时补充新 Worker、尝试终止线程池 保证 workerCount 正确,SHUTDOWN 时若队列非空可补 Worker,最终尝试转为 TERMINATED

六、getTask():从队列取任务

java 复制代码
private Runnable getTask() {
    boolean timedOut = false;
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);
        // 步骤1:池已关闭且队列空,直接返回 null
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            decrementWorkerCount(c);
            return null;
        }
        int wc = workerCountOf(c);
        // 步骤2:是否允许超时回收(非核心 或 允许核心超时)
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {
            if (compareAndDecrementWorkerCount(c))
                return null;
            continue;
        }
        try {
            // 步骤3:阻塞取任务(带超时或不带超时)
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take();
            if (r != null)
                return r;
            timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}

步骤说明

步骤 逻辑 说明 原因
1 `rs >= SHUTDOWN && (rs >= STOP workQueue.isEmpty())`
2 `timed = allowCoreThreadTimeOut wc > corePoolSize`
2.1 wc > maximumPoolSize(timed && timedOut) 线程数超限或上次取任务超时,且满足回收条件时,减少 worker 并返回 null 扩容后负载下降需缩容;超时取到 null 表示空闲过久,可回收以释放资源
3 poll(keepAliveTime) / take() 超时取或阻塞取,超时返回 null 会触发 timedOut = true,下次循环可能回收线程 核心线程用 take 永久阻塞保持存活;非核心用 poll 超时,超时后 timedOut 为 true 可能触发回收

七、整体执行流程

scss 复制代码
execute(task)
    │
    ├─ 线程数 < corePoolSize ──→ addWorker(task, true) ──→ 新建核心线程执行
    │
    ├─ 线程数 ≥ corePoolSize
    │       │
    │       ├─ workQueue.offer(task) 成功 ──→ 任务入队
    │       │       │
    │       │       └─ workerCount == 0 ──→ addWorker(null, false) 补一个空 Worker
    │       │
    │       └─ 队列满 ──→ addWorker(task, false) ──→ 新建非核心线程执行
    │               │
    │               └─ 失败 ──→ reject(task)
    │
    └─ Worker 启动 ──→ runWorker()
            │
            ├─ 执行 firstTask
            │
            └─ 循环 getTask()
                    │
                    ├─ take() / poll() 取到任务 ──→ task.run()
                    │
                    └─ 返回 null(超时或池关闭)──→ processWorkerExit()

八、状态与 ctl

java 复制代码
// ctl = 高3位状态 + 低29位线程数
RUNNING    = -1 << 29   // 接受新任务、处理队列
SHUTDOWN   =  0 << 29   // 不再接受新任务,继续处理队列
STOP       =  1 << 29   // 不再接受新任务,不处理队列,中断正在执行的任务
TIDYING    =  2 << 29   // 所有任务已结束,workerCount=0
TERMINATED =  3 << 29   // terminated() 已执行

九、小结

环节 作用
execute() 决定:新建核心线程 / 入队 / 新建非核心线程 / 拒绝
addWorker() 创建 Worker、加入 workers、启动线程
Worker 封装线程和 firstTask,实现 Runnable
runWorker() 循环取任务并执行,退出时调用 processWorkerExit
getTask() 从队列取任务,控制超时回收和池关闭时的退出
相关推荐
前端Hardy1 小时前
别再靠 Code Review 纠格式了!一套自动化前端工程化方案,让 Vue 项目提交即合规
前端·程序员·代码规范
前端Hardy1 小时前
用 uni-app x 重构我们的 App:一套代码跑通 iOS、Android、鸿蒙!人力成本直降 60%
前端·ios·uni-app
林恒smileZAZ1 小时前
告别满屏 v-if:用一个自定义指令搞定 Vue 前端权限控制
前端·javascript·vue.js
南城书生1 小时前
Android View 绘制流程
前端
南城书生1 小时前
# Android 常见内存泄漏
前端
wefly20172 小时前
M3U8 播放调试天花板!m3u8live.cn纯网页无广告,音视频开发效率直接拉满
java·前端·javascript·python·音视频
陈林梓2 小时前
异步组件、动态插槽
前端
喝咖啡的女孩2 小时前
前端巨型列表渲染
前端
兆子龙2 小时前
antd 组件也做了同款效果!深入源码看设计模式在前端组件库的应用
java·前端·架构