一、线程池核心设计思想
1.1 为什么需要线程池?
- 资源消耗大:直接创建线程(
new Thread()
)会带来内存和调度开销 - 稳定性风险:频繁创建/销毁可能导致 OOM 或系统过载
- 性能瓶颈:线程切换开销在高并发场景下显著增加
1.2 线程池核心价值
维度 | 直接创建线程 | 线程池 |
---|---|---|
资源开销 | 高(每次创建新线程) | 低(复用线程) |
响应速度 | 慢(冷启动时间) | 快(直接复用空闲线程) |
系统稳定性 | 差(易 OOM) | 高(可控的线程数量) |
任务调度 | 无 | 支持队列/优先级调度 |
1.3 默认的线程池
方法 | 底层实现(ThreadPoolExecutor 参数) | 适用场景 | 风险点 |
---|---|---|---|
newFixedThreadPool | corePoolSize = maximumPoolSize | 固定并发量 | 无界队列(LinkedBlockingQueue )可能导致 OOM |
newCachedThreadPool | corePoolSize=0 , maxPoolSize=Integer.MAX_VALUE | 短期突发任务 | 线程数爆炸(可能创建数百万线程) |
newSingleThreadExecutor | 单线程 FixedThreadPool | 顺序执行任务 | 单点故障 |
newScheduledThreadPool | 支持定时/周期性任务 | 定时任务调度 | 线程泄漏风险 |
FixedThreadPool
// 底层实现
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(
nThreads,
nThreads,
0L,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>() // 无界队列
);
}
- 特点:
- 核心线程数 = 最大线程数(固定线程数)
- 使用无界队列(
LinkedBlockingQueue
)
- 适用场景:
Web 服务器处理 HTTP 请求(固定并发量) - 风险:
队列无限增长可能导致内存溢出(OOM)
CachedThreadPool
// 底层实现
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(
0,
Integer.MAX_VALUE, // 最大线程数极大
60L,
TimeUnit.SECONDS,
new SynchronousQueue<>() // 同步移交队列
);
}
- 特点:
- 核心线程数为 0,任务直接创建新线程执行
- 空闲线程 60 秒后回收
- 适用场景:
短期突发任务(如 RPC 请求处理) - 风险:
长时间高并发会导致线程数爆炸(如提交 100 万任务会创建 100 万线程)
SingleThreadExecutor
// 底层实现
public static ExecutorService newSingleThreadExecutor() {
return new ThreadPoolExecutor(
1,
1,
0L,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<>() // 无界队列
);
}
- 特点:
- 严格单线程执行(保证任务顺序性)
- 使用无界队列
- 适用场景:
日志记录、事件监听等顺序敏感任务 - 风险:
单线程阻塞会导致整个系统停滞
ScheduledThreadPool
// 底层实现
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
// 内部实现类参数
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,
new DelayedWorkQueue());
}
- 特点:
- 支持定时/周期性任务调度
- 使用
DelayedWorkQueue
作为任务队列
- 适用场景:
定时数据备份、周期性清理任务 - 风险:
若任务执行时间超过间隔时间,可能导致任务堆积
核心问题总结
问题类型 | 具体表现 | 解决方案 |
---|---|---|
资源耗尽 | OOM(尤其是 CachedThreadPool) | 使用有界队列 + 限制最大线程数 |
队列无限增长 | 内存压力大 | 改用 ArrayBlockingQueue |
线程泄漏 | 未正确关闭导致资源无法释放 | 通过 shutdown() 优雅关闭 |
任务饥饿 | 高优先级任务被低优先级任务阻塞 | 使用优先级队列(PriorityBlockingQueue ) |
生产环境替代方案
通用型线程池配置
// 推荐的自定义配置
ThreadPoolExecutor executor = new ThreadPoolExecutor(
10, // 核心线程数
50, // 最大线程数
60L, // 空闲线程存活时间
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(1000), // 有界队列
new CustomThreadFactory(), // 自定义线程工厂
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略
);
CPU 密集型任务配置
int cpuCore = Runtime.getRuntime().availableProcessors();
ThreadPoolExecutor cpuExecutor = new ThreadPoolExecutor(
cpuCore,
cpuCore + 1,
60L,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(100) // 小队列减少上下文切换
);
IO 密集型任务配置
// 假设每个任务耗时 10ms,可支撑 100 并发
ThreadPoolExecutor ioExecutor = new ThreadPoolExecutor(
100,
200,
60L,
TimeUnit.SECONDS,
new SynchronousQueue<>()); // 直接创建线程处理
实际工程中强烈建议直接使用 ThreadPoolExecutor
自定义参数
二、ThreadPoolExecutor 核心参数(重点)
2.1 参数全景图
ThreadPoolExecutor(
int corePoolSize, // 核心线程数
int maximumPoolSize, // 最大线程数
long keepAliveTime, // 非核心线程存活时间
TimeUnit unit, // 时间单位
BlockingQueue<Runnable> workQueue, // 任务队列
ThreadFactory threadFactory, // 线程工厂
RejectedExecutionHandler handler // 拒绝策略
)
2.2 参数深度解析
(1) 核心线程数(corePoolSize)
- 关键特性:
- 即使空闲也不会被回收(除非设置
allowCoreThreadTimeOut=true
) - 线程池长期保持的最小线程数量
- 即使空闲也不会被回收(除非设置
- 配置建议:
// CPU密集型任务(如计算) int core = Runtime.getRuntime().availableProcessors() + 1; // IO密集型任务(如网络请求) int core = Runtime.getRuntime().availableProcessors() * 2;
(2) 任务队列(workQueue)
队列类型 | 特性 | 适用场景 |
---|---|---|
ArrayBlockingQueue | 有界队列,FIFO,需要指定容量 | 严格控制任务积压 |
LinkedBlockingQueue | 无界队列(默认容量 Integer.MAX_VALUE ),可能导致 OOM | 任务量可预测的场景 |
SynchronousQueue | 同步移交队列,不存储任务,直接传递给线程 | 需要立即执行的任务 |
PriorityBlockingQueue | 优先级队列,支持 Comparable 任务排序 | 需要优先处理的任务场景 |
(3) 拒绝策略(handler)
public interface RejectedExecutionHandler {
void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
策略 | 行为特点 | 适用场景 |
---|---|---|
AbortPolicy | 直接抛出 RejectedExecutionException | 需要明确感知任务拒绝的场景 |
CallerRunsPolicy | 由提交任务的线程直接执行该任务 | 平缓降级,避免雪崩效应 |
DiscardPolicy | 静默丢弃任务 | 可丢失任务的场景 |
DiscardOldestPolicy | 丢弃队列中最旧的任务,尝试重新提交当前任务 | 实时性要求高的场景 |
三、线程池工作原理解析
3.1 核心流程图解
步骤1: 提交任务 → [队列状态: 空, 线程数: 0]
步骤2: 检测核心线程数 → 未满 → 创建核心线程
步骤3: 核心线程执行 → [活跃线程: 1, 队列: 空]
步骤4: 持续提交任务 → 核心线程占满 → 任务入队
步骤5: 队列填满 → 创建非核心线程
步骤6: 非核心线程执行 → [活跃线程: maxPoolSize]
步骤7: 继续提交 → 达到上限 → 触发拒绝策略
3.2 关键执行步骤
阶段1:线程创建
// 核心线程创建逻辑
private boolean addWorker(Runnable firstTask, boolean core) {
// 1. 检查线程池状态
// 2. CAS 增加工作线程数
// 3. 创建 Worker 对象
// 4. 启动线程执行任务
}
阶段2:任务执行
// Worker 线程主循环
final void runWorker(Worker w) {
Runnable task = w.firstTask;
while (task != null || (task = getTask()) != null) {
try {
task.run(); // 执行任务
} finally {
task = null;
}
}
}
// 获取任务逻辑(关键超时控制)
private Runnable getTask() {
boolean timedOut = false;
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// 判断是否超时回收线程
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null) return r;
timedOut = true;
}
}
阶段3:线程回收
- 核心线程:默认永不过期(除非设置
allowCoreThreadTimeOut=true
) - 非核心线程:空闲时间超过
keepAliveTime
后被回收
四、线程池状态机
4.1 状态编码原理
- ctl变量:AtomicInteger 类型,高3位表示状态,低29位表示线程数
- 状态编码表:
状态名称 值(十六进制) 二进制掩码 说明 RUNNING
-536870912 1110 0000 ... 接收新任务并处理队列任务 SHUTDOWN
0 0000 0000 ... 不接收新任务,处理队列任务 STOP
536870912 1110 0000 ... 不接收新任务,中断执行中任务 TIDYING
1073741824 1000 0000 ... 所有任务终止 TERMINATED
1610612736 1100 0000 ... 终止方法执行完成
4.2 状态流转图
[*] --> RUNNING
RUNNING --> SHUTDOWN: 调用 shutdown()
RUNNING --> STOP: 调用 shutdownNow()
SHUTDOWN --> TIDYING: 队列空 + 无线程
STOP --> TIDYING: 无线程
TIDYING --> TERMINATED: terminated() 执行完成
转换路径 | 触发条件 |
---|---|
RUNNING → SHUTDOWN | 显式调用 shutdown() 方法 |
RUNNING → STOP | 显式调用 shutdownNow() 方法 |
SHUTDOWN → TIDYING | 满足以下两个条件: ① 任务队列为空 ② 工作线程数为0 |
STOP → TIDYING | 工作线程数为0 |
TIDYING → TERMINATED | 执行 terminated() 钩子方法完成(默认空实现,可自定义逻辑) |
状态 | 接收新任务 | 处理队列任务 | 中断执行中任务 | 典型场景 |
---|---|---|---|---|
RUNNING | ✅ | ✅ | ❌ | 正常处理请求 |
SHUTDOWN | ❌ | ✅ | ❌ | 优雅关闭前处理积压任务 |
STOP | ❌ | ❌ | ✅ | 紧急停止所有操作 |
TIDYING | ❌ | ❌ | ❌ | 执行资源释放前的清理工作 |
TERMINATED | ❌ | ❌ | ❌ | 线程池完全终止 |
五、实战应用案例
5.1 案例1:电商秒杀系统
// 高并发场景配置
ThreadPoolExecutor executor = new ThreadPoolExecutor(
50, // 核心线程数(初始处理能力)
200, // 最大线程数(应对流量峰值)
60L, // 空闲线程存活时间
TimeUnit.SECONDS,
new LinkedBlockingQueue<>(1000), // 缓冲队列(削峰填谷)
new ThreadPoolExecutor.CallerRunsPolicy() // 拒绝策略:调用者线程兜底
);
// 模拟10万次秒杀请求
for (int i = 0; i < 100_000; i++) {
final int taskId = i;
executor.execute(() -> {
try {
processSeckill(taskId); // 秒杀核心逻辑
} catch (Exception e) {
log.error("秒杀失败", e);
}
});
}
5.2 案例2:异步日志处理
// 使用单线程池保证日志顺序性
ExecutorService logExecutor = Executors.newSingleThreadExecutor();
public void log(String message) {
logExecutor.submit(() -> {
try (PrintWriter writer = new PrintWriter(new FileWriter("app.log", true))) {
writer.println(LocalDateTime.now() + " - " + message);
} catch (IOException e) {
// 异常处理
}
});
}
5.3 案例3:定时任务调度
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(4);
// 延迟2秒后执行,每5秒执行一次
scheduler.scheduleAtFixedRate(() -> {
backupDatabase(); // 数据库备份任务
}, 2, 5, TimeUnit.SECONDS);
六、最佳实践与避坑指南
6.1 参数配置原则
- 避免无界队列:
LinkedBlockingQueue
可能导致内存溢出 - 合理设置拒绝策略:
- 生产环境推荐
CallerRunsPolicy
或自定义策略 - 避免默认的
AbortPolicy
(直接抛出异常)
- 生产环境推荐
- 线程命名混乱,难以排查问题,推荐线程工厂规范:
static class CustomThreadFactory implements ThreadFactory { private final AtomicInteger threadNum = new AtomicInteger(1); @Override public Thread newThread(Runnable r) { Thread t = new Thread(r, "service-thread-" + threadNum.getAndIncrement()); t.setUncaughtExceptionHandler((thread, throwable) -> { log.error("线程 {} 异常终止", thread.getName(), throwable); }); return t; } }
6.2 监控与调优
// 监控关键指标
ScheduledExecutorService monitor = Executors.newSingleThreadScheduledExecutor();
monitor.scheduleAtFixedRate(() -> {
System.out.printf("[监控] 活跃线程数: %d, 队列大小: %d, 已完成任务: %d%n",
executor.getActiveCount(),
executor.getQueue().size(),
executor.getCompletedTaskCount()
);
}, 0, 5, TimeUnit.SECONDS);
6.3 优雅关闭
// 优雅停机的正确姿势
executor.shutdown(); // 停止接收新任务
try {
if (!executor.awaitTermination(30, TimeUnit.SECONDS)) {
executor.shutdownNow(); // 强制终止
if (!executor.awaitTermination(10, TimeUnit.SECONDS)) {
log.error("线程池强制终止失败");
}
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
6.4线程池参数动态调整
// 动态修改核心线程数(需通过反射实现)
Field field = ThreadPoolExecutor.class.getDeclaredField("corePoolSize");
field.setAccessible(true);
field.set(executor, newCorePoolSize);
七、高阶特性扩展
7.1 任务窃取(Work-Stealing)
// ForkJoinPool 示例(自动实现任务窃取)
ForkJoinPool pool = new ForkJoinPool();
pool.submit(() -> {
IntStream.range(0, 100)
.parallel() // 自动拆分任务
.forEach(System.out::println);
});
7.2 动态线程池(Alibaba Sentinel)
// 动态调整线程池参数(需结合监控系统)
DynamicThreadPoolExecutor dynamicExecutor = new DynamicThreadPoolExecutor(
corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(),
new DynamicRejectedExecutionHandler() // 动态拒绝策略
);
// 通过API动态调整参数
dynamicExecutor.setMaximumPoolSize(200);