线程池容量核算

线程数按任务类型核算:CPU 密集型接近核数,IO 密集型可按等待比放大;必须配队列上限与拒绝策略,防止无界队列掩盖过载。

线程池容量怎么算

线程数要按任务类型核算:CPU 密集型任务的线程数接近核数即可,再多只会增加上下文切换开销;IO 密集型任务在等待期间会让出 CPU,因此线程数可以按等待时间与计算时间的比例放大。核算出线程数之后,还要配队列上限与拒绝策略,配置才算完整。

队列与拒绝策略

无界队列会让过载表现为延迟无上限增长:请求全部堆积,没有任何失败信号,问题要等到超时时刻才暴露。有界队列配合明确的拒绝策略能让过载快速可见,再配合降级返回兜底结果。队列容量还要与上游超时联动,排队时间超过上游超时,任务即使执行完成也无人等待。

核算口径

核算口径
CPU 密集型:线程数 ≈ 核数 + 1
IO 密集型:线程数 ≈ 核数 × (1 + 平均等待 / 平均计算)
# 等待与计算比例靠实测或监控采样确定,不要凭感觉
# 队列选择:有界队列 + 明确拒绝策略(快速失败或降级)

配置示例

配置示例
cores = 运行环境可用核数
# 等待与计算比约 9:1 的 IO 密集任务
threads = cores * 10
pool = new ThreadPoolExecutor(
    threads, threads, 60, 秒,
    new ArrayBlockingQueue(2000),   # 有界队列
    new AbortPolicy())              # 拒绝策略:快速失败
# 监控:活跃线程数、队列深度、拒绝与超时计数

隔离比调参更重要

把所有下游调用放进同一个线程池,任何一个下游变慢都会占满线程,拖垮全部接口。按下游或按业务优先级拆成多个线程池,资源占用与故障影响范围都能被限定在局部,代价是总线程数上升、上下文切换变多,需要按实际容量核算。相比反复调整一个共享池的参数,隔离往往能更快地稳住核心链路。

线程数与队列容量要和上游超时联动:排队时间一旦超过上游超时,任务完成也没有人接收结果,这部分计算就是纯粹的浪费,还会挤压正常任务。

易错点

  • 一律使用无界队列,过载时延迟无上限增长且没有失败信号
  • 线程池参数与上游超时不匹配,无效计算持续堆积
  • 只调大线程数不看下游连接池上限,压力被转移而不是被吸收
  • 多个业务共用一个线程池,慢任务挤占快任务的处理能力

数据来源与参考

  • 《Java 并发编程实践》(Goetz 等)
  • 《性能之巅(第 2 版)》(Gregg)
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌