Java线程池深度解析
一、线程池的作用
线程池是Java并发编程的核心组件,主要作用包括:
- 降低资源消耗,通过重复利用已创建的线程减少线程创建和销毁的开销
- 提高响应速度,任务到达时无需等待线程创建即可执行
- 提高线程的可管理性,线程是稀缺资源,线程池可进行统一分配、调优和监控
二、线程池的核心参数
ThreadPoolExecutor有7个核心参数:
1 | public ThreadPoolExecutor(int corePoolSize, |
参数说明:
| 参数 | 说明 |
|---|---|
| corePoolSize | 核心线程数,空闲时不会被回收 |
| maximumPoolSize | 最大线程数 |
| keepAliveTime | 空闲线程存活时间 |
| workQueue | 工作队列,保存等待执行的任务 |
| threadFactory | 线程工厂,用于创建线程 |
| handler | 拒绝策略 |
三、线程池的工作流程
执行流程:
- 核心线程未满时,创建新线程执行任务
- 核心线程已满且队列未满时,任务加入队列等待
- 队列已满且线程数未达最大值时,创建非核心线程执行任务
- 队列已满且线程数已达最大值时,执行拒绝策略
1 | 任务提交 |
四、四种常见的线程池
1. FixedThreadPool
1 | ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5); |
核心线程数和最大线程数相同,使用无界队列LinkedBlockingQueue。适用于负载较重的服务器,需要限制并发线程数的场景。队列无界可能导致OOM。
2. CachedThreadPool
1 | ExecutorService cachedThreadPool = Executors.newCachedThreadPool(); |
核心线程数为0,最大线程数为Integer.MAX_VALUE,使用SynchronousQueue。适用于执行短期异步任务的小程序或负载较轻的服务器。线程数可能无限增长导致OOM。
3. SingleThreadExecutor
1 | ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor(); |
只有一个核心线程,使用无界队列LinkedBlockingQueue。适用于需要顺序执行任务的场景。队列无界可能导致OOM。
4. ScheduledThreadPool
1 | ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5); |
支持定时和周期性任务执行。适用于需要延迟执行或定期执行任务的场景。
五、拒绝策略
队列满且线程数达到最大值时触发拒绝策略:
| 策略 | 行为 |
|---|---|
| AbortPolicy | 抛出RejectedExecutionException异常 |
| CallerRunsPolicy | 由调用线程执行任务 |
| DiscardPolicy | 直接丢弃任务 |
| DiscardOldestPolicy | 丢弃队列中最老的任务并重新提交 |
六、线程池参数配置
线程数设置
CPU密集型任务:
1 | 线程数 = CPU核心数 + 1 |
IO密集型任务:
1 | 线程数 = CPU核心数 * (1 + 平均等待时间/平均工作时间) |
混合型任务可拆分为CPU密集型和IO密集型,分别使用不同线程池。
队列选择
| 队列类型 | 特点 | 适用场景 |
|---|---|---|
| ArrayBlockingQueue | 有界队列 | 防止资源耗尽 |
| LinkedBlockingQueue | 可选有界/无界 | 吞吐量要求高 |
| SynchronousQueue | 不存储元素 | 每个任务立即执行 |
| PriorityBlockingQueue | 优先级队列 | 任务有优先级需求 |
七、线程池监控
ThreadPoolExecutor提供的监控方法:
1 | ThreadPoolExecutor executor = new ThreadPoolExecutor(...); |
八、最佳实践
1. 避免使用Executors创建线程池
FixedThreadPool和SingleThreadExecutor使用无界队列可能导致OOM。CachedThreadPool允许创建Integer.MAX_VALUE个线程可能导致OOM。建议直接使用ThreadPoolExecutor构造函数创建,明确指定队列大小。
2. 合理配置线程数
根据任务类型设置线程数,生产环境通过压测确定最优配置。
3. 使用有界队列
防止任务堆积导致OOM,配合拒绝策略保证系统稳定性。
4. 自定义线程工厂
1 | ThreadFactory threadFactory = r -> { |
5. 选择合适的拒绝策略
关键任务使用CallerRunsPolicy保证任务不丢失,非关键任务使用DiscardPolicy或DiscardOldestPolicy。
6. 正确关闭线程池
1 | executor.shutdown(); |
九、应用示例
Spring Boot配置线程池
1 |
|
使用线程池执行异步任务
1 |
|
十、总结
线程池是Java并发编程的核心组件。使用线程池可以提高系统性能和响应速度,降低资源消耗,提供线程管理能力。
核心要点:
- 理解线程池的工作原理和参数含义
- 根据任务类型配置线程数
- 使用有界队列防止OOM
- 选择合适的拒绝策略
- 做好线程池监控和关闭
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 夏天的风吹向哪里!
