跳到主要内容

线程池调优

问题

如何合理配置线程池参数?线程池队列堆积、任务被拒绝怎么处理?

答案

线程池七大参数

new ThreadPoolExecutor(
corePoolSize, // 核心线程数
maximumPoolSize, // 最大线程数
keepAliveTime, // 空闲线程存活时间
TimeUnit, // 时间单位
workQueue, // 工作队列
threadFactory, // 线程工厂
handler // 拒绝策略
);

参数配置经验

场景corePoolSizemaximumPoolSize队列
CPU 密集型CPU 核数 + 1同 core较短队列
IO 密集型CPU 核数 × 2CPU 核数 × 4中等队列
混合型按 IO 等待比计算core × 2视吞吐而定
理论公式
线程数 = CPU 核数 × (1 + IO 等待时间 / CPU 计算时间)

例:8 核 CPU,接口 90% 时间在等 IO(DB/Redis/HTTP)
线程数 = 8 × (1 + 0.9/0.1) = 8 × 10 = 80
不要迷信公式

公式只是起点。生产环境要以压测数据为准——逐步调大线程数,观察吞吐量和 RT 的变化,找到最优点。

队列选择

队列特点适用场景
LinkedBlockingQueue无界(默认)/ 有界通用,必须设上限
ArrayBlockingQueue有界,公平/非公平需要严格控制
SynchronousQueue不存储,直接交付要求快速响应
无界队列的风险

new LinkedBlockingQueue()(无界)→ 任务堆积 → 内存溢出 → OOM。生产环境必须设置有界队列

拒绝策略

策略行为适用场景
AbortPolicy抛异常(默认)需要明确感知过载
CallerRunsPolicy调用者线程执行不允许丢弃
DiscardPolicy静默丢弃可丢弃的非核心任务
DiscardOldestPolicy丢弃队首新的比旧的更重要

动态线程池

基于 Nacos 动态调参
@NacosConfigListener(dataId = "thread-pool.yaml")
public void onConfigChange(String config) {
ThreadPoolConfig poolConfig = yaml.loadAs(config, ThreadPoolConfig.class);

// ThreadPoolExecutor 支持运行时修改参数
executor.setCorePoolSize(poolConfig.getCoreSize());
executor.setMaximumPoolSize(poolConfig.getMaxSize());

// 调整队列容量需自定义 ResizableLinkedBlockingQueue
if (executor.getQueue() instanceof ResizableLinkedBlockingQueue) {
((ResizableLinkedBlockingQueue<?>) executor.getQueue())
.setCapacity(poolConfig.getQueueSize());
}

log.info("线程池参数已更新: core={}, max={}, queue={}",
poolConfig.getCoreSize(), poolConfig.getMaxSize(), poolConfig.getQueueSize());
}

线程池监控

核心监控指标
@Scheduled(fixedRate = 10000)
public void monitor() {
ThreadPoolExecutor pool = taskExecutor;
log.info("线程池状态 - 活跃:{}/{}, 队列:{}/{}, 已完成:{}, 已拒绝:{}",
pool.getActiveCount(), // 当前活跃线程
pool.getPoolSize(), // 当前线程总数
pool.getQueue().size(), // 队列中等待的任务
pool.getQueue().remainingCapacity(),
pool.getCompletedTaskCount(), // 已完成任务数
pool.getRejectedExecutionHandler() // 拒绝策略
);
}

告警阈值建议:

  • 队列使用率 > 80% → 预警
  • 活跃线程数 = 最大线程数 → 预警
  • 出现 RejectedExecution → 告警

常见面试问题

Q1: 线程池参数怎么设置?

答案

没有银弹。先按公式估算(CPU 密集 = N+1、IO 密集 = 2N),再通过压测逐步调整。不同业务场景的参数不同,要根据实际吞吐量和 RT 来确定。

详见 线程池

Q2: 线程池的工作流程是什么?

答案

  1. 提交任务 → 当前线程数 < corePoolSize → 创建核心线程
  2. 核心线程满 → 加入队列
  3. 队列满 → 创建非核心线程(不超过 maxPoolSize)
  4. 都满了 → 触发拒绝策略

Q3: 为什么阿里规约不允许用 Executors?

答案

  • Executors.newFixedThreadPool()LinkedBlockingQueue() 无界队列 → OOM
  • Executors.newCachedThreadPool()maximumPoolSize = Integer.MAX_VALUE → 创建过多线程 → OOM
  • 应该手动 new ThreadPoolExecutor() 指定所有参数

Q4: Spring @Async 底层用的什么线程池?

答案

默认用 SimpleAsyncTaskExecutor(每次 new 一个线程,不复用!),生产环境必须自定义:

@Configuration
public class AsyncConfig {
@Bean("taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(50);
executor.setQueueCapacity(200);
executor.setRejectedExecutionHandler(new CallerRunsPolicy());
executor.setThreadNamePrefix("async-");
return executor;
}
}

相关链接