报表导出服务把 corePoolSize 设成 CPU 核数 8,maximumPoolSize 也是 8,下午跑批时队列里堆了 3000 个任务,最久的一个等了 26 分钟。第一次遇到会往复杂处想,其实漏掉的多是个小前提。

jcmd 1 Thread.print | grep -c 'pool-1-thread'
jstat -gcutil 1 1000 5

核数只反映并行计算能力,这批任务大部分时间在等数据库和对象存储,线程多数处于等待状态。核心线程数按核数设,等于把并发上限锁在 8,后面的任务只能排队。这一点先确认清楚,后面就不用反复试。

「核心线程数按核数」这个问题文档写得含糊,实际行为以当前版本为准,别照旧版本的结论套。新版本先放一台机器上跑一天再全量,问题一般在这段时间暴露。

具体做法是,用 jstack 看这 8 个线程的状态 确认是 RUNNABLE 还是大量 WAITING;估算 IO 等待占比 超过一半就把核心线程数往上调;核心和最大线程数分开设 核心 32 最大 64 队列给上限;压同样的批次量 观察队列长度和单任务耗时是否回落。

改完照着核一遍,队列长度峰值从 3000 降到 200 以内,同一批报表 18 分钟跑完。

作者 9823189