深入理解 Java 线程池(二):ThreadPoolExecutor 执行流程 + 运行状态 + ctl 原理全解析

我们继续深入到 ThreadPoolExecutor 的内部运行机制,彻底搞清楚:

  • 任务提交后线程池到底怎么处理?
  • 线程池内部有哪些状态?
  • ctl 是如何用 32 位整数保存"线程池状态 + 线程数量"的?
  • shutdown()、shutdownNow() 区别到底是什么?
  • worker 是怎么管理线程的?

理解这篇,你就真正掌握了 Java 线程池的灵魂。

一、线程池处理任务的完整流程(最重要的知识点)

当你执行:

executor.execute(task);

线程池会按照 3 个阶段 处理任务。

我给你画一张清晰流程图👇(重点)

① 有空闲核心线程?→ 直接创建 Worker 执行任务

② 核心线程满了 → 放入队列

③ 队列也放不下 → 尝试创建"非核心线程(临时线程)"

① 有空闲核心线程?→ 直接创建 Worker 执行任务

java 复制代码
if (workerCount < corePoolSize) {
    创建核心线程执行任务
    return
}

核心线程数不足 → 创建新的核心线程。

② 核心线程满了 → 放入队列

java 复制代码
if (workQueue.offer(task)) {
    // 队列接收成功
}
  • 队列可以无限放(LinkedBlockingQueue)
  • 或有界(ArrayBlockingQueue)

如果队列能放下 → 线程池不会增加线程

③ 队列也放不下 → 尝试创建"非核心线程(临时线程)"

java 复制代码
if (workerCount < maximumPoolSize) {
    创建非核心线程执行任务
    return
}

如果还不行 → 线程池触发 拒绝策略

二、线程池的五大状态(必须背下来)

线程池状态非常关键,比如**shutdown() 和 shutdownNow()**的区别就来自这里。

线程池有 五种状态(按优先级由大到小):

1)RUNNING

  • 接收新任务
  • 执行队列任务

正常工作状态。

2)SHUTDOWN

  • 不再接收新任务
  • 继续执行队列中的任务

调用:

executor.shutdown();

进入 SHUTDOWN。

3)STOP

  • 不接收新任务
  • 不执行队列任务
  • 中断正在执行的线程

调用:

executor.shutdownNow();

线程池进入 STOP。

4)TIDYING

  • 所有任务已终止
  • workerCount == 0
  • 正在调用 terminated()

5)TERMINATED

  • 线程池彻底结束

三、最核心:ctl 的位运算机制(线程池状态是怎么存的?)

ThreadPoolExecutor 非常巧妙地使用了一个 int ctl 保存两类信息:

  • 高 3 位:线程池状态(RUNNING / SHUTDOWN / ...)
  • 低 29 位:当前 worker 数量

一个 ctl = 线程池状态 + 工作线程数

ctl 的结构(极其关键)

java 复制代码
  3 bit        29 bit
 ┌─────┬──────────────────────┐
 │状态 │    workerCount        │
 └─────┴──────────────────────┘

源码中:

private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

ctl 为什么要设计成 1 个 int?

因为:

  • 状态和线程数经常一起判断
  • 原子操作必须一次 CAS,同时判断两个值
  • 合并到一个 int 可以极大提高效率

例如:

线程池在 execute(task) 时必须检查:

  • 线程池状态是否 RUNNING?
  • workerCount 是否小于 corePoolSize?

这两个判断可以用一次 ctl CAS 完成。

状态的取值(高 3 bit)

源码:

java 复制代码
private static final int COUNT_BITS = 29;
private static final int RUNNING    = -1 << COUNT_BITS;
private static final int SHUTDOWN   =  0 << COUNT_BITS;
private static final int STOP       =  1 << COUNT_BITS;
private static final int TIDYING    =  2 << COUNT_BITS;
private static final int TERMINATED =  3 << COUNT_BITS;

直接背下来:

  • RUNNING 的高 3 位是 111
  • SHUTDOWN 是 000
  • STOP 是 001
  • TIDYING 是 010
  • TERMINATED 是 011

如何从 ctl 读状态?

runStateOf(ctl.get())

如何读线程数?

workerCountOf(ctl.get())

四、worker 是怎么管理线程的?

ThreadPoolExecutor 内部有个 Worker 类:

java 复制代码
private final class Worker extends AbstractQueuedSynchronizer
        implements Runnable

Worker 本质上是:

  • 一个线程 + AQS(可重入锁)
  • 线程由 ThreadFactory 创建
  • AQS 用来控制线程中断行为

Worker 生命周期:

  1. 创建 Worker(携带第一个任务)
  2. 执行第一个任务
  3. 从队列中不断获取任务
  4. 空闲超过 keepAliveTime → 线程退出

Worker 退出后 workerCount 减 1。

五、线程池在 shutdown() 时内部做了什么?

调用:

executor.shutdown();

线程池会:

  1. 将状态从 RUNNING 改为 SHUTDOWN
  2. 不再接收新任务
  3. 继续执行队列任务
  4. workerCount 逐渐变为 0
  5. 最终进入 TIDYING → TERMINATED

重要 :shutdown 不会中断正在执行的线程

六、shutdownNow() 做了什么?

  1. 将状态从 RUNNING 改为 STOP

  2. 中断所有正在执行任务的线程

  3. 清空任务队列,并返回未执行任务列表

  4. workerCount 减到 0

  5. 进入 TERMINATED

区别总结:

方法 状态 是否中断任务 是否执行队列任务
shutdown() SHUTDOWN
shutdownNow() STOP

七、线程池完整运行流程图(非常关键)

java 复制代码
             提交任务 execute()
                       │
                       ▼
         ┌──────────────────────────┐
         │ 1. workerCount < core?   │
         └───────────────┬──────────┘
                         是│否
                          ▼
                创建核心线程执行
                          │
                          ▼
         队列是否可放下任务?
               │               │
              是              否
               ▼               ▼
     任务入队,等待线程执行   workerCount < maximum?
                                   │             │
                                  是            否
                                   ▼             ▼
                   创建临时线程执行        执行拒绝策略

八、总结(背下来就是线程池高手)

  • ThreadPoolExecutor 使用 ctl(高 3 位状态 + 低 29 位线程数) 管理线程池
  • 线程池有五种状态:RUNNING → SHUTDOWN → STOP → TIDYING → TERMINATED
  • execute() 的任务处理流程是三步:
  1. corePoolSize 能否创建线程?
  2. 队列能否接收任务?
  3. maximumPoolSize 能否创建线程?
  • shutdown() 和 shutdownNow() 的根本区别:是否中断 worker、是否执行队列
  • Worker 是线程池的运行单元,内部用 AQS 控制线程终止逻辑

下一篇:

Java 线程池(第三篇):拒绝策略与生产级线程池调优指南

相关推荐
咖啡续命又一天2 小时前
Trae CN IDE 中 Python 开发的具体流程和配置总结
开发语言·ide·python·ai编程
哈哈老师啊2 小时前
Springboot学生综合测评系统hxtne(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
java·数据库·spring boot
4311媒体网2 小时前
帝国cms调用文章内容 二开基本操作
java·开发语言·php
GSDjisidi3 小时前
东京IT软件会社-(株)GSD|多种技术栈募集,高度人才+20分
开发语言·面试·职场和发展
zwxu_3 小时前
Nginx NIO对比Java NIO
java·nginx·nio
程序员zgh3 小时前
Linux系统常用命令集合
linux·运维·服务器·c语言·开发语言·c++
獭.獭.4 小时前
C++ -- STL【unordered_set与unordered_map的实现】
开发语言·c++·unordered_map·unordered_set
可观测性用观测云4 小时前
Pyroscope Java 接入最佳实践
java
山海青风4 小时前
语音合成 - 用 Python 合成藏语三大方言语音
开发语言·python·音视频