Java 线程池实战指南
线程池通过复用线程、控制并发和统一管理,显著提升系统性能与资源利用率。
一、核心价值
| 价值 | 说明 |
|---|---|
| 降低开销 | 避免频繁创建/销毁线程(单次约 10ms) |
| 流量控制 | 防止突发任务压垮系统 |
| 统一管理 | 支持线程状态跟踪、任务队列监控 |
二、核心参数
2.1 ThreadPoolExecutor 构造函数
public ThreadPoolExecutor(
int corePoolSize, // 核心线程数
int maximumPoolSize, // 最大线程数
long keepAliveTime, // 空闲存活时间
TimeUnit unit, // 时间单位
BlockingQueue<Runnable> workQueue, // 任务队列
ThreadFactory threadFactory, // 线程工厂
RejectedExecutionHandler handler // 拒绝策略
)
2.2 参数详解
| 参数 | 作用 | 配置建议 |
|---|---|---|
| corePoolSize | 核心线程数(常驻) | CPU 密集型:CPU 核数 +1 IO 密集型:CPU 核数×2 |
| maximumPoolSize | 最大线程数(应急) | 根据系统负载调整 |
| keepAliveTime | 非核心线程存活时间 | 30 秒~数分钟 |
| workQueue | 任务队列 | 有界队列防 OOM |
| threadFactory | 线程工厂 | 自定义命名方便排查 |
| handler | 拒绝策略 | CallerRunsPolicy |
三、工作流程
flowchart TD
A[提交任务] --> B{核心线程<br>满?}
B -->|否 | C[创建核心线程]
B -->|是 | D{队列<br>满?}
D -->|否 | E[任务入队]
D -->|是 | F{最大线程<br>满?}
F -->|否 | G[创建非核心线程]
F -->|是 | H[触发拒绝策略]
流程说明:
- 核心线程处理:
< corePoolSize创建新线程 - 任务入队:核心线程满,放入队列
- 扩容:队列满且
< maximumPoolSize,创建非核心线程 - 拒绝:队列和线程池均满,触发拒绝策略
四、拒绝策略
4.1 内置策略
| 策略 | 行为 | 适用场景 |
|---|---|---|
| AbortPolicy | 抛出异常 | 需快速失败的关键业务 |
| CallerRunsPolicy | 调用者线程执行 | 允许降级的非核心任务 |
| DiscardPolicy | 静默丢弃 | 可丢失的日志、统计 |
| DiscardOldestPolicy | 丢弃最旧任务 | 实时性要求高的场景 |
4.2 自定义策略
public class CustomRejectPolicy implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
// 1. 记录日志
log.warn("任务被拒绝:{}", r.toString());
// 2. 持久化到数据库
saveToDatabase(r);
// 3. 发送告警
sendAlert();
}
}
五、线程池类型
5.1 Executors 工厂方法
// ❌ 不推荐使用(可能 OOM)
ExecutorService fixed = Executors.newFixedThreadPool(10);
// 问题:LinkedBlockingQueue 无界
ExecutorService cached = Executors.newCachedThreadPool();
// 问题:maximumPoolSize = Integer.MAX_VALUE
ExecutorService single = Executors.newSingleThreadExecutor();
// 问题:异常后无法恢复
5.2 推荐配置
// CPU 密集型任务
ThreadPoolExecutor cpuPool = new ThreadPoolExecutor(
Runtime.getRuntime().availableProcessors() + 1,
Runtime.getRuntime().availableProcessors() + 1,
60L, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1000),
new CustomThreadFactory("cpu-pool"),
new ThreadPoolExecutor.CallerRunsPolicy()
);
// IO 密集型任务
ThreadPoolExecutor ioPool = new ThreadPoolExecutor(
Runtime.getRuntime().availableProcessors() * 2,
Runtime.getRuntime().availableProcessors() * 2,
60L, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(2000),
new CustomThreadFactory("io-pool"),
new ThreadPoolExecutor.CallerRunsPolicy()
);
六、最佳实践
6.1 优雅关闭
public void shutdown() {
executor.shutdown(); // 停止接收新任务
try {
// 等待已提交任务完成
if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
executor.shutdownNow(); // 强制中断
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
}
6.2 监控指标
// 活跃线程数
int active = executor.getActiveCount();
// 队列积压
int queueSize = executor.getQueue().size();
// 已完成任务
long completed = executor.getCompletedTaskCount();
// 累计任务
long total = executor.getTaskCount();
6.3 线程池隔离
// 不同业务使用独立线程池
ThreadPoolExecutor orderPool = createPool("order");
ThreadPoolExecutor paymentPool = createPool("payment");
ThreadPoolExecutor notificationPool = createPool("notification");
// 避免相互影响
七、常见问题
7.1 线程泄漏
问题:任务超时未完成,线程无法回收
解决:
// 设置任务超时
Future<?> future = executor.submit(task);
future.get(5, TimeUnit.SECONDS); // 超时抛出异常
// 移除耗时任务
executor.remove(task);
7.2 死锁
问题:嵌套提交任务到同一线程池
解决:
// 使用不同线程池
executor1.submit(() -> {
executor2.submit(() -> {
// 避免死锁
});
});
八、总结
线程池核心要点:
- 核心参数:corePoolSize、maximumPoolSize、workQueue
- 工作流程:核心线程 → 队列 → 非核心线程 → 拒绝
- 拒绝策略:优先使用 CallerRunsPolicy
- 最佳实践:避免 Executors 工厂,手动创建 ThreadPoolExecutor
- 监控告警:活跃线程数、队列积压、拒绝次数
生产环境务必手动创建线程池,精细化控制参数,避免 OOM 风险。