要提高 ThreadPoolTaskExecutor 处理并发任务的能力,需从线程池配置、任务优化和系统资源管理三方面入手
1. 合理配置线程池参数(核心优化点)
@Bean
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 核心参数配置
executor.setCorePoolSize(10); // 核心线程数(长期保留的线程)
executor.setMaxPoolSize(50); // 最大线程数(突发流量时扩容)
executor.setQueueCapacity(100); // 队列容量(缓冲任务)
executor.setKeepAliveSeconds(60); // 非核心线程空闲存活时间(秒)
executor.setThreadNamePrefix("task-"); // 线程名前缀(便于监控)
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); // 拒绝策略
executor.initialize();
return executor;
}
- 参数调优原则:
- CPU 密集型任务(如计算):
核心线程数 ≈ CPU 核数
(如 Runtime.getRuntime().availableProcessors()
)。 - I/O 密集型任务(如网络请求、DB 操作):
核心线程数 = CPU核数 * (1 + 平均等待时间/平均计算时间)
,通常设为 2 * CPU核数
。 - 队列容量:避免无界队列(
Integer.MAX_VALUE
)导致 OOM,需根据系统负载设置合理阈值。 - 最大线程数:作为系统保护阈值,需预留资源余量(如不超过系统总线程限制)。
2. 优化任务执行逻辑
- 拆分大任务:将耗时任务分解为多个小任务,避免阻塞线程。
- 异步非阻塞:
- 使用
CompletableFuture
或 @Async
实现嵌套异步。 - I/O 操作改用非阻塞库(如 Netty、异步数据库驱动)。
- 避免线程竞争:
- 减少同步锁(
synchronized
),改用并发集合(ConcurrentHashMap
)。 - 使用
ThreadLocal
存储线程私有数据。
3. 拒绝策略优化
当任务超出 队列容量 + 最大线程数
时,选择合适的拒绝策略:
// 推荐策略:
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
CallerRunsPolicy
:由提交任务的线程执行任务(如 Tomcat 线程),避免雪崩。- 其他策略:
AbortPolicy
(默认):抛出 RejectedExecutionException
。DiscardPolicy
:静默丢弃任务。DiscardOldestPolicy
:丢弃队列最旧任务。
4. 线程池监控与动态调整
- 监控关键指标:
ThreadPoolExecutor pool = executor.getThreadPoolExecutor();
int activeCount = pool.getActiveCount(); // 活动线程数
long completedTaskCount = pool.getCompletedTaskCount(); // 已完成任务数
int queueSize = pool.getQueue().size(); // 队列积压数
- 动态调参(Spring Boot 2.2+):
// 动态修改核心线程数(需在运行时调用)
executor.setCorePoolSize(20);
executor.getThreadPoolExecutor().prestartCoreThread(); // 立即生效
5. 资源与系统级优化
- JVM 参数调整:
- 增加栈空间:
-Xss256k
(避免线程过多导致栈溢出)。 - 堆空间预留:确保队列积压时不会 OOM。
- 操作系统限制:
- 检查 Linux 线程数限制:
ulimit -u
,必要时调整 /etc/security/limits.conf
。
- 避免资源泄漏:
- 任务中捕获所有异常,防止线程因异常退出。
- 使用
ThreadPoolTaskExecutor#setWaitForTasksToCompleteOnShutdown(true)
确保停机时任务完成。
6. 高级场景优化
- 分优先级队列:
继承
ThreadPoolTaskExecutor
,重写 createQueue()
返回优先级队列(如 PriorityBlockingQueue
)。 - 线程池隔离:
对不同业务使用独立线程池(如订单、支付服务分离),避免互相影响。
- 背压机制:
集成响应式编程(如 Reactor)控制任务提交速率。
验证与压测
- 使用 JMeter 或 Gatling 模拟并发请求。
- 监控指标:
- 线程数波动(
activeCount
)。 - 任务完成耗时。
- GC 频率与停顿时间。
- 调整参数直至系统吞吐量(QPS)和延迟(RT)达标。
关键点:没有普适的最优参数,需结合业务特点(任务耗时、并发量)和系统资源(CPU、内存)反复压测调整。