Java线程池 - 内部线程管理机制详解

线程池对线程的管理,核心在于 Worker 内部类ctl 状态控制变量。下面从创建、运行、销毁三个维度拆解。


一、Worker:线程池里的"线程包装器"

线程池并不直接持有 Thread 对象,而是包装成 Worker

复制代码
private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
    final Thread thread;      // 真正干活的线程
    Runnable firstTask;       // 第一个任务(可能为 null)
    volatile long completedTasks;  // 已完成任务计数
    
    Worker(Runnable firstTask) {
        setState(-1);         // AQS 状态:-1 表示禁止中断,直到 runWorker
        this.firstTask = firstTask;
        this.thread = getThreadFactory().newThread(this);  // 创建线程
    }
    
    public void run() {
        runWorker(this);      // 线程启动后进入这里
    }
}

Worker 继承 AQS 的巧妙设计

Worker 继承 AbstractQueuedSynchronizer 不是为了排队,而是用 AQS 的状态来实现一个不可重入的互斥锁

状态 含义
-1 初始化中,禁止中断
0 未加锁(空闲)
1 已加锁(正在执行任务)

线程池通过 tryLock() 来判断 Worker 是否空闲,从而决定是否中断它。


二、线程创建:addWorker() 方法

当任务提交后,线程池判断需要创建新线程时,调用 addWorker()

复制代码
private boolean addWorker(Runnable firstTask, boolean core) {
    // 1. CAS 尝试增加线程计数(ctl 的低29位)
    retry:
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);  // 获取运行状态
        
        // 状态检查:如果已 SHUTDOWN 且队列非空,不再创建
        if (rs >= SHUTDOWN && !(rs == SHUTDOWN && firstTask == null && !workQueue.isEmpty()))
            return false;
            
        for (;;) {
            int wc = workerCountOf(c);  // 当前线程数
            // 容量检查:core 为 true 检查 corePoolSize,否则检查 maximumPoolSize
            if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize))
                return false;
            if (compareAndIncrementWorkerCount(c))  // CAS 增加线程计数
                break retry;
            c = ctl.get();
            if (runStateOf(c) != rs)
                continue retry;  // 状态变了,重试外层循环
        }
    }
    
    // 2. 真正创建 Worker 并启动线程
    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
        w = new Worker(firstTask);      // 创建 Worker(内部创建 Thread)
        final Thread t = w.thread;
        if (t != null) {
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                // 再次检查状态,将 Worker 加入 workers HashSet
                workers.add(w);
                workerAdded = true;
            } finally {
                mainLock.lock();
            }
            if (workerAdded) {
                t.start();              // 🚀 启动线程!
                workerStarted = true;
            }
        }
    } finally {
        if (!workerStarted)
            addWorkerFailed(w);         // 启动失败,回滚
    }
    return workerStarted;
}

创建流程图

复制代码
任务提交
   ↓
判断需要创建线程
   ↓
CAS 增加 ctl 线程计数(原子操作)
   ↓
new Worker(firstTask) → ThreadFactory.newThread(this)
   ↓
加 mainLock → workers.add(w)
   ↓
thread.start() → 执行 Worker.run() → runWorker(this)

三、线程运行:runWorker() 方法

线程启动后进入 runWorker(),这是线程的核心生命周期循环

复制代码
final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock();  // 允许中断(state 从 -1 变为 0)
    
    boolean completedAbruptly = true;
    try {
        // 🔥 核心循环:先执行 firstTask,然后不断从队列取任务
        while (task != null || (task = getTask()) != null) {
            w.lock();  // 加锁,标记为"工作中"
            
            // 检查线程池状态,必要时中断自己
            if ((runStateAtLeast(ctl.get(), STOP) ||
                 (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP)))
                && !wt.isInterrupted())
                wt.interrupt();
            
            try {
                beforeExecute(wt, task);  // 钩子方法,可自定义
                Throwable thrown = null;
                try {
                    task.run();           // 🎯 真正执行任务!
                } catch (RuntimeException x) {
                    thrown = x; throw x;
                } catch (Error x) {
                    thrown = x; throw x;
                } catch (Throwable x) {
                    thrown = x; throw new Error(x);
                } finally {
                    afterExecute(task, thrown);  // 钩子方法
                }
            } finally {
                task = null;
                w.completedTasks++;
                w.unlock();  // 解锁,标记为"空闲"
            }
        }
        completedAbruptly = false;  // 正常退出循环
    } finally {
        processWorkerExit(w, completedAbruptly);  // 线程退出处理
    }
}

关键点

  • while 循环 :线程不会执行完一个任务就死,而是循环取任务,实现线程复用

  • getTask():从队列阻塞获取任务,这是线程"保活"或"销毁"的分水岭

  • w.lock()/unlock() :通过 AQS 锁标识线程是否在工作,便于 shutdown() 时区分"空闲线程"和"忙碌线程"


四、线程销毁:核心在 getTask()processWorkerExit()

4.1 getTask():决定线程是等待还是销毁

复制代码
private Runnable getTask() {
    boolean timedOut = false;
    
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);
        
        // 1. 线程池状态检查:STOP / SHUTDOWN 且队列为空 → 返回 null,线程销毁
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            decrementWorkerCount();  // 线程计数 -1
            return null;
        }
        
        int wc = workerCountOf(c);
        
        // 2. 判断是否需要超时回收
        // allowCoreThreadTimeOut 默认为 false
        // 如果线程数 > corePoolSize,或者允许核心线程超时,则 timed = true
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
        
        // 3. 回收条件:
        //    (wc > maximumPoolSize 或 超时) 且 (wc > 1 或 队列为空)
        if ((wc > maximumPoolSize || (timed && timedOut))
            && (wc > 1 || workQueue.isEmpty())) {
            if (compareAndDecrementWorkerCount(c))
                return null;  // 返回 null → 线程即将销毁
            continue;
        }
        
        try {
            // 4. 从队列取任务
            // timed = true  → poll(keepAliveTime)  带超时阻塞
            // timed = false → take()               无限阻塞
            Runnable r = timed ?
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take();
                
            if (r != null)
                return r;
            timedOut = true;   // 超时了,下次循环可能触发回收
        } catch (InterruptedException retry) {
            timedOut = false;  // 被中断,重试
        }
    }
}

销毁逻辑图解

复制代码
runWorker 循环
   ↓
getTask()
   ├── 线程池 STOP / (SHUTDOWN 且队列为空)
   │      └── return null → processWorkerExit() → 线程销毁
   │
   ├── 线程数 > corePoolSize(或 allowCoreThreadTimeOut=true)
   │      └── workQueue.poll(keepAliveTime) 
   │            ├── 超时未取到 → return null → 销毁
   │            └── 取到任务 → 继续执行
   │
   └── 线程数 ≤ corePoolSize(默认)
          └── workQueue.take() → 无限阻塞等待 → 线程保活

4.2 processWorkerExit():线程退出清理

复制代码
private void processWorkerExit(Worker w, boolean completedAbruptly) {
    // 1. 如果是异常退出,线程计数需要补减
    if (completedAbruptly)
        decrementWorkerCount();
    
    final ReentrantLock mainLock = this.mainLock;
    mainLock.lock();
    try {
        completedTaskCount += w.completedTasks;  // 累加完成的任务数
        workers.remove(w);                        // 从 workers 集合移除
    } finally {
        mainLock.unlock();
    }
    
    // 2. 尝试终止线程池(如果是 SHUTDOWN 且队列为空,或 STOP)
    tryTerminate();
    
    // 3. 如果线程池还在运行,且线程数 < corePoolSize,补充线程
    int c = ctl.get();
    if (runStateLessThan(c, STOP)) {
        if (!completedAbruptly) {  // 正常退出
            int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
            if (min == 0 && !workQueue.isEmpty())
                min = 1;
            if (workerCountOf(c) >= min)
                return;  // 不需要补充
        }
        addWorker(null, false);  // 补充一个线程(不带任务,从队列取)
    }
}

五、核心线程 vs 非核心线程:本质区别

核心线程 非核心线程
创建时机 任务提交时,线程数 < corePoolSize 队列满时,线程数 < maximumPoolSize
保活机制 workQueue.take() 无限阻塞 workQueue.poll(keepAliveTime) 超时阻塞
回收条件 默认不回收(除非 allowCoreThreadTimeOut(true) 空闲超过 keepAliveTime 自动回收
本质 没有本质区别 ,都是 Worker 对象,只是回收策略不同

线程池内部没有"核心线程"和"非核心线程"的区分标记 ,纯粹是通过 wc > corePoolSize 这个判断来决定使用 take() 还是 poll()


六、ctl:一个变量控制所有状态

复制代码
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// ctl = 高3位(运行状态) + 低29位(线程数量)

private static final int COUNT_BITS = Integer.SIZE - 3;  // 29
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;  // 29位最大值

// 状态常量(高3位)
private static final int RUNNING    = -1 << COUNT_BITS;  // 111
private static final int SHUTDOWN   =  0 << COUNT_BITS;  // 000
private static final int STOP       =  1 << COUNT_BITS;  // 001
private static final int TIDYING    =  2 << COUNT_BITS;  // 010
private static final int TERMINATED =  3 << COUNT_BITS;  // 011
状态 说明
RUNNING 接收新任务,处理队列任务
SHUTDOWN 不接收新任务,但处理队列中剩余任务(调用 shutdown()
STOP 不接收新任务,不处理队列任务,中断正在执行的任务(调用 shutdownNow()
TIDYING 所有任务完成,线程数为 0,准备执行 terminated() 钩子
TERMINATED terminated() 执行完毕

七、总结:线程生命周期全景图

复制代码
┌─────────────────────────────────────────────────────────────┐
│                      线程池 ThreadPoolExecutor               │
├─────────────────────────────────────────────────────────────┤
│  任务提交                                                    │
│     ↓                                                        │
│  addWorker(firstTask, core)                                  │
│     ↓                                                        │
│  new Worker(firstTask) → ThreadFactory.newThread(this)       │
│     ↓                                                        │
│  thread.start()                                              │
│     ↓                                                        │
│  Worker.run() → runWorker(Worker)                              │
│     ↓                                                        │
│  ┌─────────────────────────────────────┐                     │
│  │  while (task != null || getTask() != null)                │
│  │     ↓                                                      │
│  │  w.lock() → task.run() → w.unlock()  ← 循环复用            │
│  │     ↓                                                      │
│  │  getTask() 返回 null → 退出循环                            │
│  └─────────────────────────────────────┘                     │
│     ↓                                                        │
│  processWorkerExit(Worker, completedAbruptly)                  │
│     ↓                                                        │
│  workers.remove(Worker) → 线程被 GC 回收                    │
└─────────────────────────────────────────────────────────────┘

高频追问

Q:shutdown() 后线程池里的线程去哪了?

调用 shutdown() 后状态变为 SHUTDOWN,不再接收新任务。线程池会中断所有空闲线程 (通过 interruptIdleWorkers(),利用 Worker 的 AQS 锁判断空闲),让它们从 getTask() 的阻塞中醒来。醒来的线程发现状态是 SHUTDOWN 且队列为空,就会 return null 退出。

Q:shutdownNow() 为什么能立即停止任务?

状态变为 STOP,然后中断所有线程(无论空闲还是忙碌),清空队列。正在执行的任务如果响应中断会立即退出,不响应中断的则继续执行直到完成。

Q:如何设置核心线程也超时回收?

复制代码
executor.allowCoreThreadTimeOut(true);

这样所有线程都用 poll(keepAliveTime) 取任务,超时后全部回收,线程池可以收缩到 0 个线程。

相关推荐
开开心心就好20 分钟前
手机悬屏翻译工具外语游戏漫画APP全覆盖
android·前端·javascript·python·游戏·pdf·html
恋猫de小郭37 分钟前
AI 时代,也许你的 Flutter 需要一套 Dartastic OpenTelemetry 监控
android·前端·flutter
Kapaseker1 小时前
我为什么喜欢 Compose ?
android·kotlin
JMchen1231 小时前
【Android 性能优化实战 60 讲】03 Perfetto 现代分析利器:SQL 查询 Trace 数据,微秒级定位函数 CPU 耗时
android·sql·性能优化·实战·源码分析·perfetto·启动优化
开开心心就好2 小时前
手机精确倒计时工具悬浮窗口秒数实时显示
android·前端·javascript·jupyter·智能手机·pdf·html
墨狂之逸才2 小时前
一次 Android 工位机黑屏卡死排查:根因不是 App,而是 scrcpy 与 Rockchip 编码器的 DMA-BUF 泄漏
android·android studio
Privasa-隐私实验室2 小时前
跨端技术选型|Flutter搭建隐私加密App,安卓与iOS双端差异适配实战
android·笔记·安全·flutter·ios·隐私安全·aes-256
limuyang29 小时前
PDF Viewer KMP(基于 chrome 的 PDFium 内核)
android·kotlin
心平气和量大福大13 小时前
android-控件-搜索框SearchView
android·gitee