Java 线程池核心指南
📚 文档定位
本文档是 Java 线程池的全面基础指南,聚焦于 JDK 原生ThreadPoolExecutor的核心原理和使用。配套文档:
Java线程池状态管理完整指南 - 深度篇
- 线程池5种状态的底层实现原理
- 状态转换的详细时序和源码分析
tryTerminate()和ctl变量的设计精髓- 适合需要深入理解状态机制的读者
Spring线程池状态管理指南 - 实战篇
- Spring 对线程池的封装(
ThreadPoolTaskExecutor)- Spring 容器生命周期与线程池集成
- 优雅关闭的最佳实践
- 实际项目案例分析
建议阅读顺序:
- 必读:本文档(核心基础)
- 可选深入:状态管理完整指南(深度理解)
- Spring项目:Spring状态管理指南(实战应用)
一、概述
1.1 什么是线程池
线程池是一种多线程处理模式,预先创建线程来执行任务,实现线程复用,避免频繁创建销毁线程的开销。
1.2 核心优势
| 优势 | 说明 |
|---|---|
| 降低资源消耗 | 复用已创建线程,减少创建和销毁开销 |
| 提高响应速度 | 任务无需等待线程创建,可立即执行 |
| 提高可管理性 | 统一分配、调优和监控线程 |
| 扩展功能 | 支持定时、周期执行等高级功能 |
二、核心类结构
Executor (接口)
└── ExecutorService (接口)
└── AbstractExecutorService (抽象类)
└── ThreadPoolExecutor (核心实现)
└── ScheduledThreadPoolExecutor (定时任务)
三、ThreadPoolExecutor 七大参数
3.1 构造方法
public ThreadPoolExecutor(
int corePoolSize, // ① 核心线程数
int maximumPoolSize, // ② 最大线程数
long keepAliveTime, // ③ 空闲存活时间
TimeUnit unit, // ④ 时间单位
BlockingQueue workQueue, // ⑤ 任务队列
ThreadFactory threadFactory, // ⑥ 线程工厂
RejectedExecutionHandler handler // ⑦ 拒绝策略
)
3.2 参数详解
| 参数 | 说明 | 重要程度 |
|---|---|---|
| corePoolSize | 核心线程数,即使空闲也不销毁 | ⭐⭐⭐ |
| maximumPoolSize | 线程池最大线程数 | ⭐⭐⭐ |
| keepAliveTime | 非核心线程空闲存活时间 | ⭐⭐ |
| unit | keepAliveTime 的时间单位 | ⭐ |
| workQueue | 任务等待队列 | ⭐⭐⭐ |
| threadFactory | 创建线程的工厂 | ⭐⭐ |
| handler | 任务拒绝策略 | ⭐⭐⭐ |
3.3 任务队列类型
| 队列类型 | 边界 | 特点 | 适用场景 |
|---|---|---|---|
ArrayBlockingQueue | 有界 | 数组实现,FIFO | 控制队列大小 |
LinkedBlockingQueue | 可选 | 链表实现,默认无界 | 一般用途 |
SynchronousQueue | 无容量 | 直接交接,不存储 | 高吞吐量场景 |
PriorityBlockingQueue | 无界 | 支持优先级排序 | 优先级调度 |
DelayQueue | 无界 | 支持延迟执行 | 定时任务 |
3.4 四种拒绝策略
| 策略 | 行为 | 适用场景 |
|---|---|---|
| AbortPolicy (默认) | 抛出 RejectedExecutionException | 需要感知任务被拒绝 |
| CallerRunsPolicy | 由调用线程执行任务 | 不希望丢失任务,可降速 |
| DiscardPolicy | 静默丢弃新任务 | 可接受任务丢失 |
| DiscardOldestPolicy | 丢弃队列最老任务 | 优先执行新任务 |
四、任务执行流程
4.1 流程图
┌─────────────────────────────────────────────────────────┐
│ 提交任务 │
└────────────────────────┬────────────────────────────────┘
↓
┌────────────────────────┴────────────────────────────────┐
│ 当前线程数 < corePoolSize ? │
└────────────────────────┬────────────────────────────────┘
↓ 是 ↓ 否
┌───────────┴───────────┐ ┌────────────┴────────────────┐
│ 创建核心线程执行任务 │ │ 任务队列未满 ? │
└───────────────────────┘ └────────────┬────────────────┘
↓ 是 ↓ 否
┌──────────┴──────┐ ┌───────┴────────────────┐
│ 加入任务队列 │ │ 当前线程数 < maxPoolSize? │
└─────────────────┘ └───────┬────────────────┘
↓ 是 ↓ 否
┌────────────┴────┐ ┌────┴────────┐
│ 创建非核心线程执行 │ │ 执行拒绝策略 │
└─────────────────┘ └─────────────┘
4.2 执行示例
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, // corePoolSize = 2
5, // maximumPoolSize = 5
60L, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(10) // 队列容量 = 10
);
// 提交 1-2: 创建核心线程执行
// 提交 3-12: 进入队列等待
// 提交 13-15: 创建非核心线程执行 (达到最大5)
// 提交 16+: 触发拒绝策略
4.3 ⚠️ 执行顺序陷阱
重要:后提交的任务可能比队列中的任务更早执行!
任务 3~12 在队列中等待
任务 13 → 创建线程3 立即执行 ← 比任务3更早!
任务 14 → 创建线程4 立即执行 ← 比任务3更早!
任务 15 → 创建线程5 立即执行 ← 比任务3更早!
直接原因:队列满时创建非核心线程执行新任务,而非从队列取任务。
设计原因(Doug Lea 的设计选择):
| 设计目标 | 说明 |
|---|---|
| 吞吐量优先 | 尽可能让更多任务被处理,而不是严格保证顺序 |
| 资源弹性 | 队列满说明压力大,此时创建线程比排队更能缓解压力 |
| 避免死锁 | 某些场景下严格排队可能导致任务相互依赖死锁 |
| 快速响应 | 新创建的线程可以立即处理任务,减少整体等待时间 |
影响场景:
| 场景 | 是否受影响 |
|---|---|
| 独立任务(如日志记录、异步通知) | ✅ 无影响,顺序不重要 |
| 有依赖关系的任务 | ⚠️ 可能有问题 |
| 需要严格 FIFO 的业务(如订单处理) | ❌ 会有问题 |
解决方案:
- 需要严格 FIFO:设置
corePoolSize = maxPoolSize(不创建非核心线程) - 高吞吐场景:使用
SynchronousQueue(无队列,直接交接) - 严格顺序:使用
newSingleThreadExecutor()(单线程顺序执行)
4.4 工作原理深度解析
4.4.1 队列任务的消费机制
队列采用生产者-消费者模式,任务持续被消费,而非等待所有任务到齐后才执行:
时间线示例:
T1: 核心线程1 执行任务1
队列: [任务3, 任务4, ..., 任务12] (10个任务等待)
T2: 核心线程1 完成任务1
→ 立即从队列头部取出任务3 ✅
→ 开始执行任务3
队列: [任务4, 任务5, ..., 任务12]
T3: 核心线程2 完成任务2
→ 立即从队列头部取出任务4 ✅
→ 开始执行任务4
队列: [任务5, 任务6, ..., 任务12]
T4: 所有线程继续从队列竞争获取任务...
关键点:
- ✅ 线程完成任务后立即从队列取下一个任务
- ✅ 队列采用 FIFO(先进先出)策略
- ✅ 所有线程(核心+非核心)都参与队列任务消费
- ✅ 线程之间竞争获取队列任务
等待时间估算:
场景:核心线程都在忙,队列中有 N 个任务
公式:队列末尾任务的等待时间 ≈ (N / 核心线程数) × 单任务平均耗时
示例:
- 核心线程数 = 2
- 队列中有 10 个任务
- 单个任务平均耗时 = 5秒
- 队列末尾任务等待时间 ≈ (10 / 2) × 5 = 25秒
4.4.2 非核心线程的完整生命周期
阶段1:创建时机
当队列满了且当前线程数 < maxPoolSize 时,创建非核心线程直接执行触发创建的那个新任务:
场景:corePoolSize=2, maxPoolSize=5, queueCapacity=10
队列状态: [任务3, 任务4, ..., 任务12] (已满)
提交任务13
↓
队列满了 → 创建非核心线程3
↓
非核心线程3 直接执行任务13 ✅ (不是从队列取的)
阶段2:工作循环
非核心线程执行完当前任务后,进入工作循环,从队列获取任务:
// 非核心线程的工作循环(简化版)
while (true) {
// 尝试从队列获取任务(带超时 keepAliveTime)
Runnable task = workQueue.poll(keepAliveTime, TimeUnit.SECONDS);
if (task != null) {
// 获取到任务,执行并继续循环
runTask(task);
// 继续下一轮循环
} else {
// 超时未获取到任务,线程退出
break; // 线程被销毁
}
}
阶段3:清理机制(倒计时)
非核心线程完成当前任务
↓
尝试从队列获取任务(带超时 keepAliveTime)
↓
┌───┴──────────────────────────────────┐
│ 队列有任务? │
│ 是 → 取出任务执行 │
│ → 继续工作循环 │
│ │
│ 否 → 开始倒计时 (keepAliveTime=60秒) │
│ → 等待期间持续尝试获取任务 │
└───┬──────────────────────────────────┘
↓
倒计时期间:
├─ 获取到任务?
│ 是 → 执行任务 → 继续工作循环
│ 否 → 继续等待
│
└─ 倒计时结束(60秒超时)
→ 线程被销毁 ✅
关键理解:
| 问题 | 答案 | 说明 |
|---|---|---|
| 队列空了,非核心线程会立即被销毁吗? | ❌ 否 | 需要空闲超过 keepAliveTime |
| 非核心线程会等队列清空才被清理吗? | ❌ 否 | 只要自己空闲超时就被清理 |
| 倒计时期间获取到任务会怎样? | 继续执行,不销毁 | 倒计时重置 |
| 核心线程也会倒计时吗? | ❌ 默认不会 | 除非设置 allowCoreThreadTimeOut(true) |
4.4.3 非核心线程与外部请求的关系
误区澄清:
❌ 错误理解:
非核心线程执行完任务 → 等待新请求 → 外部新请求直接分配给它
✅ 正确理解:
非核心线程执行完任务 → 只能从队列取任务(竞争)
外部新请求 → 由线程池调度器独立判断(创建新线程 or 进队列 or 拒绝)
完整流程示例:
配置:corePoolSize=2, maxPoolSize=5, queueCapacity=10
时刻T1: 2个核心线程在工作,队列满了(10个任务)
时刻T2: 外部提交任务13
→ 队列满了 → 创建非核心线程3
→ 非核心线程3 直接执行任务13 ✅
时刻T3: 非核心线程3 执行完任务13
→ 从队列取任务(竞争) → 取到任务A
→ 执行任务A
时刻T4: 同时,外部提交任务14
→ 线程池判断:核心线程忙、队列满、线程数<5
→ 创建非核心线程4 直接执行任务14 ✅
(不等待非核心线程3空闲)
时刻T5: 队列逐渐被消费,最终清空
时刻T6: 非核心线程3 执行完当前任务
→ 尝试从队列取任务 → 队列空
→ 开始倒计时 keepAliveTime(60秒)
时刻T7: 倒计时30秒时,外部提交新任务100
→ 核心线程都在忙 → 任务进入队列
→ 非核心线程3 从队列抢到任务100 ✅
→ 继续执行,不被销毁
时刻T8: 又过了一段时间,队列再次清空
→ 非核心线程3 空闲倒计时60秒
→ 核心线程还在忙
→ 60秒后非核心线程3被销毁 ✅
4.4.4 线程的任务获取方式总结
| 问题 | 答案 | 备注 |
|---|---|---|
| 外部新任务只会进入队列吗? | ❌ 否 | 可能:直接被新线程执行、进队列、被拒绝 |
| 线程执行完任务后只会从队列取任务? | ✅ 是 | 线程无法主动获取外部新任务 |
| 非核心线程创建时做什么? | 直接执行触发创建的任务 | 不是从队列取任务 |
| 线程会等队列清空吗? | ❌ 否 | 持续消费,边执行边取 |
4.4.5 实战示例对比
示例1:独立任务场景
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, 5, 60L, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(10)
);
// 提交 20 个独立任务
for (int i = 1; i <= 20; i++) {
executor.execute(() -> {
System.out.println("任务执行: " + Thread.currentThread().getName());
});
}
// 执行过程:
// 任务1-2: 创建核心线程1-2,直接执行
// 任务3-12: 进入队列等待 (10个)
// 任务13-17: 创建非核心线程3-7,直接执行 (达到maxPoolSize=5)
// 任务18-20: 触发拒绝策略
// 注意:任务13-17 比任务3-12 更早执行!
示例2:需要严格FIFO的场景
// 解决方案:设置 corePoolSize = maxPoolSize
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5, 5, 60L, TimeUnit.SECONDS, // core = max = 5
new ArrayBlockingQueue<>(100)
);
// 提交 20 个任务
for (int i = 1; i <= 20; i++) {
executor.execute(() -> {
System.out.println("任务执行: " + Thread.currentThread().getName());
});
}
// 执行过程:
// 任务1-5: 创建5个核心线程,直接执行
// 任务6-20: 全部进入队列,严格按FIFO顺序执行 ✅
// 不会创建非核心线程,保证了顺序性
五、线程池五种状态
💡 深度阅读
本章节提供线程池状态的基础概览。如需深入理解状态管理机制,包括:
- 详细的状态转换时序和触发条件
tryTerminate()的完整源码解析shutdownNow()后的两种状态可能性- 底层
ctl变量的设计原理- 常见问题和误区澄清
请阅读:Java线程池状态管理完整指南
5.1 状态说明
| 状态 | 值 | 接受新任务 | 处理队列任务 | 触发方式 |
|---|---|---|---|---|
| RUNNING | -1 | ✅ | ✅ | 初始状态 |
| SHUTDOWN | 0 | ❌ | ✅ | shutdown() |
| STOP | 1 | ❌ | ❌ | shutdownNow() |
| TIDYING | 2 | ❌ | ❌ | 自动转换 |
| TERMINATED | 3 | ❌ | ❌ | 自动转换 |
5.2 状态转换
RUNNING ──shutdown()──→ SHUTDOWN ──(队列空,线程=0)──→ TIDYING ──→ TERMINATED
│ │ ↑
│ └──shutdownNow()──→ STOP ───┘
│ ↑
└─────────shutdownNow()──────────────────────┘
关键点:
- ✅
shutdownNow()直接跳到 STOP 状态(不经过 SHUTDOWN) - ⚠️ TIDYING 和 TERMINATED 是自动转换,由
tryTerminate()完成 - 🔄 状态只能单向递增:RUNNING < SHUTDOWN < STOP < TIDYING < TERMINATED
5.3 shutdown() vs shutdownNow()
| 方法 | 状态转换 | 新任务 | 队列任务 | 正在执行的任务 | 返回值 |
|---|---|---|---|---|---|
shutdown() | → SHUTDOWN | 拒绝 | 继续执行 | 继续执行 | void |
shutdownNow() | → STOP | 拒绝 | 不执行 | 尝试中断 | List |
六、常用线程池类型
6.1 四种预定义线程池
| 类型 | 创建方式 | 核心线程 | 最大线程 | 队列 | 特点 |
|---|---|---|---|---|---|
| Fixed | newFixedThreadPool(n) | n | n | 无界 | 固定线程数 |
| Cached | newCachedThreadPool() | 0 | MAX | 同步队列 | 自动伸缩 |
| Single | newSingleThreadExecutor() | 1 | 1 | 无界 | 顺序执行 |
| Scheduled | newScheduledThreadPool(n) | n | MAX | 延迟队列 | 定时执行 |
6.2 详细配置
// 1. FixedThreadPool - 固定线程数
new ThreadPoolExecutor(n, n, 0L, MILLISECONDS, new LinkedBlockingQueue<>())
// 2. CachedThreadPool - 弹性线程池
new ThreadPoolExecutor(0, MAX_VALUE, 60L, SECONDS, new SynchronousQueue<>())
// 3. SingleThreadExecutor - 单线程
new ThreadPoolExecutor(1, 1, 0L, MILLISECONDS, new LinkedBlockingQueue<>())
// 4. ScheduledThreadPool - 定时任务
new ScheduledThreadPoolExecutor(corePoolSize)
6.3 ⚠️ 风险警告
| 类型 | 风险 | 原因 |
|---|---|---|
| FixedThreadPool | OOM | 无界队列可能无限增长 |
| CachedThreadPool | OOM | 可能创建大量线程 |
| SingleThreadExecutor | OOM | 无界队列可能无限增长 |
建议:生产环境手动创建 ThreadPoolExecutor,使用有界队列。
七、代码示例
7.1 创建线程池
ThreadPoolExecutor executor = new ThreadPoolExecutor(
10, // 核心线程数
20, // 最大线程数
60L, TimeUnit.SECONDS, // 空闲存活时间
new ArrayBlockingQueue<>(100), // 有界队列
new ThreadFactoryBuilder()
.setNameFormat("worker-%d")
.build(), // 线程工厂
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
7.2 提交任务
// 无返回值
executor.execute(() -> {
System.out.println("执行任务");
});
// 有返回值
Future future = executor.submit(() -> {
return "任务结果";
});
String result = future.get(); // 阻塞获取结果
// 带超时获取
String result = future.get(5, TimeUnit.SECONDS);
7.3 优雅关闭
public void gracefulShutdown(ExecutorService executor) {
// 1. 停止接受新任务
executor.shutdown();
try {
// 2. 等待已提交任务完成
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
// 3. 超时强制关闭
List dropped = executor.shutdownNow();
log.warn("强制关闭,丢弃 {} 个任务", dropped.size());
// 4. 再次等待
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
log.error("线程池无法终止");
}
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
}
7.4 监控状态
public void monitorPool(ThreadPoolExecutor executor) {
log.info("核心线程数: {}", executor.getCorePoolSize());
log.info("当前线程数: {}", executor.getPoolSize());
log.info("活跃线程数: {}", executor.getActiveCount());
log.info("最大线程数: {}", executor.getMaximumPoolSize());
log.info("队列任务数: {}", executor.getQueue().size());
log.info("已完成任务: {}", executor.getCompletedTaskCount());
log.info("总任务数: {}", executor.getTaskCount());
}
八、线程池大小设置
8.1 计算公式
| 任务类型 | 公式 | 说明 |
|---|---|---|
| CPU 密集型 | N + 1 | N = CPU 核心数 |
| IO 密集型 | N × 2 或 N / (1 - 阻塞系数) | 阻塞系数 0.8~0.9 |
| 混合型 | 拆分或取中间值 | 建议拆分为两个线程池 |
典型场景示例:
| 类型 | 场景 | 特点 | 线程数建议 (8核CPU) |
|---|---|---|---|
| CPU 密集型 | 数学计算、加密解密、图像处理、视频编码、JSON解析、正则匹配 | CPU 利用率高,几乎无等待 | 8+1 = 9 |
| IO 密集型 | 数据库查询、HTTP 请求、文件读写、RPC 调用、消息队列消费 | 大量时间等待 IO,CPU 空闲 | 8×2 = 16 或更高 |
| 混合型 | Web 请求处理(含计算+数据库)、报表生成、数据 ETL | 既有计算又有 IO | 拆分为两个池 |
为什么这样定义?
| 类型 | 核心思想 | 详细解释 |
|---|---|---|
| CPU 密集型 = N+1 | 充分利用 CPU,防止空闲 | 线程数=核心数时 CPU 已满载;+1 是为了应对偶发中断(缺页、GC),确保有线程立即补位 |
| IO 密集型 = N×2+ | 用线程数弥补等待时间 | 线程等待 IO 时 CPU 空闲,多线程可让 CPU 切换执行其他任务,减少空闲 |
图解 CPU 密集型(N+1 的意义):
8核 CPU,线程数=8 时:
线程3 发生缺页中断 → 核心3 空闲 → CPU 利用率下降
8核 CPU,线程数=9 时:
线程3 发生缺页中断 → 线程9 立即补位 → 核心3 继续工作 → CPU 利用率保持 100%
图解 IO 密集型(为什么需要更多线程):
假设:任务 80% 时间等待 IO,20% 时间计算
时间线 →→→→→→→→→→→→→→→→→→→→→→→→
线程1: [计算][====等待IO====][计算][====等待IO====]
线程2: [计算][====等待IO====][计算]...
线程3: [计算][====等待IO====]...
...
一个线程只用 20% 的 CPU → 一个核心可服务 5 个线程
8 核 × 5 = 40 个线程,才能让 CPU 不空闲
公式推导:
IO 密集型线程数 = CPU核心数 / (1 - 阻塞系数)
= CPU核心数 / CPU实际使用率
示例:任务总耗时 100ms,其中 80ms 等待数据库
阻塞系数 = 80 / 100 = 0.8
CPU使用率 = 1 - 0.8 = 0.2 (20%)
线程数 = 8 / 0.2 = 40
验证:40 个线程 × 20% CPU = 8 核 CPU 满载 ✓
8.2 获取 CPU 核心数
int cpuCores = Runtime.getRuntime().availableProcessors();
8.3 经验值参考
| 场景 | 核心线程 | 最大线程 | 队列容量 |
|---|---|---|---|
| Web 服务器 | CPU × 2 | CPU × 4 | 100-500 |
| 批处理任务 | CPU | CPU × 2 | 1000+ |
| 高 IO 操作 | CPU × 4 | CPU × 8 | 500-1000 |
九、Spring Boot 集成
💡 提示:本章节仅展示基础配置示例。关于 Spring 线程池的详细内容,包括:
ThreadPoolTaskExecutor的状态管理机制- Spring 容器生命周期与线程池的集成
- 优雅关闭的最佳实践
- 实际项目案例分析
请参考:Spring线程池状态管理指南
9.1 基础配置示例
@Configuration
@EnableAsync
public class ThreadPoolConfig {
@Bean("taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(20);
executor.setQueueCapacity(200);
executor.setThreadNamePrefix("async-");
executor.setKeepAliveSeconds(60);
// 拒绝策略
executor.setRejectedExecutionHandler(
new ThreadPoolExecutor.CallerRunsPolicy()
);
// 优雅关闭(详细说明见 Spring线程池状态管理指南)
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(60);
executor.initialize();
return executor;
}
}
9.3 异步方法调用
@Service
public class AsyncService {
@Async("taskExecutor")
public void asyncTask() {
// 异步执行,无返回值
}
@Async("taskExecutor")
public Future asyncTaskWithResult() {
// 异步执行,返回 Future
return new AsyncResult<>("结果");
}
@Async("taskExecutor")
public CompletableFuture asyncCompletable() {
// 异步执行,返回 CompletableFuture
return CompletableFuture.completedFuture("结果");
}
}
十、最佳实践清单
✅ 推荐做法
- 手动创建线程池,不使用 Executors 工厂方法
- 使用有界队列,防止 OOM
- 自定义线程名称,便于问题排查
- 设置合适的拒绝策略
- 优雅关闭线程池
- 监控线程池状态
- 合理设置线程池大小
❌ 避免做法
- 不要使用无界队列 (
LinkedBlockingQueue无参构造) - 不要设置过大的线程池
- 不要忽略拒绝策略
- 不要忘记关闭线程池
- 不要在生产环境不监控
- 不要假设关闭是同步的
📋 配置模板
// 生产环境推荐配置模板
ThreadPoolExecutor executor = new ThreadPoolExecutor(
Runtime.getRuntime().availableProcessors() * 2, // 核心线程
Runtime.getRuntime().availableProcessors() * 4, // 最大线程
60L, TimeUnit.SECONDS, // 空闲时间
new ArrayBlockingQueue<>(200), // 有界队列
new ThreadFactoryBuilder()
.setNameFormat("biz-pool-%d")
.setUncaughtExceptionHandler((t, e) ->
log.error("线程异常: {}", t.getName(), e))
.build(),
new ThreadPoolExecutor.CallerRunsPolicy() // 调用者执行
);
// 允许核心线程超时
executor.allowCoreThreadTimeOut(true);
十一、常见问题
Q1: 为什么不推荐 Executors?
| 方法 | 问题 |
|---|---|
newFixedThreadPool | 使用无界队列,可能导致 OOM |
newCachedThreadPool | 最大线程数为 MAX_VALUE,可能创建大量线程 |
newSingleThreadExecutor | 使用无界队列,可能导致 OOM |
Q2: 任务执行异常怎么处理?
// 方式1:submit + Future.get()
Future future = executor.submit(task);
try {
future.get();
} catch (ExecutionException e) {
log.error("任务异常", e.getCause());
}
// 方式2:自定义 ThreadFactory 设置 UncaughtExceptionHandler
new ThreadFactoryBuilder()
.setUncaughtExceptionHandler((t, e) -> log.error("异常", e))
.build();
// 方式3:重写 afterExecute
executor = new ThreadPoolExecutor(...) {
@Override
protected void afterExecute(Runnable r, Throwable t) {
if (t != null) log.error("任务异常", t);
}
};
Q3: 如何确保任务不丢失?
- 使用
CallerRunsPolicy拒绝策略 - 持久化任务队列(数据库/消息队列)
- 处理
shutdownNow()返回的未执行任务
Q4: 线程池能重启吗?
不能。状态转换是单向的,必须创建新实例。
十二、下一步学习
📖 推荐阅读路径
根据你的需求选择下一步阅读内容:
路径1:深入理解状态管理 🔍
适合想深入理解线程池状态机制的读者,你将学到:
- ✅ 详细的状态转换时序和触发条件
- ✅
tryTerminate()的完整源码解析 - ✅
shutdownNow()后的两种状态可能性分析 - ✅ 底层
ctl变量的巧妙设计 - ✅ 10+ 个常见问题和误区澄清
路径2:Spring项目实战 🚀
适合在 Spring/Spring Boot 项目中使用线程池的读者,你将学到:
- ✅
ThreadPoolTaskExecutor如何封装ThreadPoolExecutor - ✅ Spring 容器如何自动管理线程池生命周期
- ✅ 如何配置优雅关闭(
await-termination) - ✅ 如何监控线程池状态(Actuator 集成)
- ✅ 实际项目中的最佳实践和案例分析
📊 完整学习路径
Java线程池核心指南 (当前)
↓ ↓
┌──────────┴────────┐ └─────────────┐
↓ ↓ ↓
状态管理深度解析 Spring实战应用 直接项目实践
↓ ↓ ↓
状态管理完整指南 Spring状态管理指南 ↓
↓ ↓ ↓
└──────────┬────────┘──────────────────────┘
↓
综合项目实践

0 评论
评论