线程池
一、线程池概述
是什么?
线程池是一种管理多线程的机制,通过预先创建一组线程并复用它们执行任务,避免频繁创建/销毁线程的开销。核心组件包括工作队列、线程集合、任务提交接口和拒绝策略。
解决什么问题
- 资源消耗:减少线程创建/销毁的系统开销(CPU、内存)
- 响应速度:任务到达时直接使用空闲线程执行,无需等待线程创建
- 可控性:限制并发线程数量,防止系统过载
- 线程管理:统一管理线程生命周期和异常处理
应用场景
- Web服务器处理请求
- 批量数据处理(如日志分析)
- 异步任务执行(如发送邮件)
- 高并发场景(如电商秒杀)
二、核心参数(ThreadPoolExecutor)
JDK中通过 ThreadPoolExecutor
类配置线程池,关键参数:
- corePoolSize(核心线程数):常驻线程数量,即使空闲也不销毁
- maximumPoolSize(最大线程数):允许创建的最大线程数
- keepAliveTime(空闲线程存活时间):非核心线程空闲时的存活时长
- unit(时间单位):如
TimeUnit.SECONDS
- workQueue(任务队列):存放待执行任务的阻塞队列
- threadFactory(线程工厂):自定义线程创建逻辑
- rejectedExecutionHandler(拒绝策略):任务过多时的处理规则
Java示例
java
ThreadPoolExecutor executor = new ThreadPoolExecutor(
5, // corePoolSize
10, // maximumPoolSize
60, // keepAliveTime
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(100), // 任务队列容量100
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy() // 拒绝策略
);
三、工作原理
- 提交任务:调用
execute(Runnable task)
提交任务 - 流程判断:
- 若当前线程数 < corePoolSize → 创建新线程执行
- 若线程数 ≥ corePoolSize → 任务入队
- 若队列满且线程数 < maxPoolSize → 创建新线程
- 若队列满且线程数 ≥ maxPoolSize → 触发拒绝策略
- 线程复用:线程执行完任务后从队列获取新任务
- 空闲回收:非核心线程空闲超过 keepAliveTime 后被回收
四、拒绝策略
当任务队列满且线程数达到 maxPoolSize 时触发:
策略类型 | 实现类 | 行为 | 适用场景 |
---|---|---|---|
AbortPolicy | ThreadPoolExecutor.AbortPolicy | 抛出 RejectedExecutionException | 需严格监控的任务 |
CallerRunsPolicy | ThreadPoolExecutor.CallerRunsPolicy | 由提交任务的线程直接执行 | 避免任务丢失的慢速场景 |
DiscardPolicy | ThreadPoolExecutor.DiscardPolicy | 静默丢弃新任务 | 可容忍丢失的任务(如日志) |
DiscardOldestPolicy | ThreadPoolExecutor.DiscardOldestPolicy | 丢弃队列中最旧的任务,重试提交 | 允许牺牲旧任务的场景 |
Java示例
java
// 自定义拒绝策略:记录日志后丢弃任务
RejectedExecutionHandler customHandler = (task, executor) -> {
System.err.println("Task rejected: " + task);
};
五、线程池种类(Executors工厂类)
JDK提供快速创建线程池的方法(但生产环境建议手动配置 ThreadPoolExecutor
):
FixedThreadPool
javaExecutorService fixedPool = Executors.newFixedThreadPool(5);
- 固定线程数(corePoolSize = maxPoolSize)
- 使用无界队列
LinkedBlockingQueue
- 风险:队列堆积可能导致OOM
CachedThreadPool
javaExecutorService cachedPool = Executors.newCachedThreadPool();
- 线程数无上限(maxPoolSize=Integer.MAX_VALUE)
- 空闲线程60秒回收
- 风险:线程过多可能导致CPU过载
SingleThreadExecutor
javaExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
- 单线程串行执行
- 适用于需顺序执行的任务
ScheduledThreadPool (JDK8+)
javaScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(3); scheduledPool.scheduleAtFixedRate(() -> System.out.println("Task"), 1, 5, TimeUnit.SECONDS);
- 支持定时/周期性任务
- 核心参数:
corePoolSize
+DelayedWorkQueue
六、重要注意事项
- 资源耗尽风险:
FixedThreadPool
/SingleThreadExecutor
使用无界队列 → 可能OOMCachedThreadPool
线程数无上限 → 可能CPU/内存过载
- 线程泄漏:确保任务正确处理异常,避免线程因异常退出
- 监控工具:
- 使用
executor.getPoolSize()
监控线程数 - 通过JMX或Spring Actuator监控线程池状态
- 使用
- 合理配置:
- CPU密集型任务:线程数 ≈ CPU核心数
- IO密集型任务:线程数 ≈ CPU核心数 * (1 + 等待时间/计算时间)
- 优雅关闭:java
executor.shutdown(); // 停止接收新任务 executor.awaitTermination(10, TimeUnit.SECONDS); // 等待已有任务完成
七、总结
维度 | 要点 |
---|---|
核心价值 | 降低资源开销、提高响应速度、控制并发度 |
关键配置 | corePoolSize 、maxPoolSize 、workQueue 、rejectedExecutionHandler |
拒绝策略 | AbortPolicy(默认)、CallerRunsPolicy等4种 |
线程池类型 | Fixed/Cached/SingleThread/Scheduled |
最佳实践 | 生产环境手动配置 ThreadPoolExecutor ,避免使用无界队列 |
💡 建议:JDK8+推荐使用
CompletableFuture
结合线程池实现异步编程,或使用Spring的@Async
注解简化开发。