corePoolSize和maximumPoolSize需按任务类型设定:CPU密集型为availableProcessors()+1,IO密集型建议2×availableProcessors()并压测验证,混合型优先按IO估算;二者大小关系必须满足core≤max,否则抛IllegalArgumentException。

corePoolSize 和 maximumPoolSize 怎么设才不踩坑
这两个参数决定线程池的“弹性边界”,但很多人直接套用 Runtime.getRuntime().availableProcessors() 就完事,结果在 IO 密集型任务里吞吐暴跌。
关键看任务类型:
- CPU 密集型:设为
availableProcessors() + 1,避免线程频繁切换 - IO 密集型(如 HTTP 调用、DB 查询):通常设为
2 × availableProcessors()起步,实际需压测验证;可更高,因为线程常阻塞在等待响应上 - 混合型:优先按 IO 密集估算,再通过
ThreadPoolExecutor.getCompletedTaskCount()和监控(如 activeCount)反推是否过载
注意:corePoolSize > maximumPoolSize 会直接抛 IllegalArgumentException,这是硬校验,不是运行时逻辑。
keepAliveTime 设多少?别只盯着“空闲线程存活时间”字面意思
keepAliveTime 只对超出 corePoolSize 的那部分线程生效——也就是说,如果 corePoolSize = 5、maximumPoolSize = 10,那只有第 6~10 个线程会在空闲超时后被回收;前 5 个默认一直活着(除非显式设置 allowCoreThreadTimeOut(true))。
立即学习“Java免费学习笔记(深入)”;
常见误配:
- IO 密集场景设了 60 秒,但流量波峰间隔只有 10 秒 → 线程反复创建销毁,GC 压力大
- 长周期定时任务用固定大小线程池,却开了
allowCoreThreadTimeOut→ 核心线程意外退出,下次触发时要重建,延迟不可控
建议:IO 密集型设 10~60 秒;CPU 密集型可设 60 秒以上,或干脆不设超时(保持 allowCoreThreadTimeOut = false)。
拒绝策略(RejectedExecutionHandler)不是兜底,是业务信号
用 AbortPolicy(默认)在线上等于把异常扔给调用方,容易掩盖容量瓶颈;CallerRunsPolicy 表面“缓解压力”,实则让业务线程卡住,可能引发雪崩。
真正可控的做法:
- 自定义策略记录日志 + 上报指标(如 Prometheus counter),例如:
public class LoggingRejectHandler implements RejectedExecutionHandler { private final Counter rejectedCounter = Counter.builder("threadpool.rejected").register(Metrics.globalRegistry); @Override public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) { rejectedCounter.increment(); log.warn("Task rejected: {}, pool: {}", r.getClass().getSimpleName(), executor.getPoolSize()); } } - 对非核心任务(如日志异步刷盘),可用
DiscardPolicy;但必须确保丢弃不影响主流程一致性 - 绝不在线程池满时自动扩容(比如动态改
setCorePoolSize),这会破坏预估容量和监控基线
为什么 Executors.newFixedThreadPool(10) 是线上禁用写法
它底层用的是 LinkedBlockingQueue,且队列容量是 Integer.MAX_VALUE。一旦任务提交速度持续超过消费速度,队列无限堆积,最终 OOM。
正确姿势是显式构造,控制队列行为:
- 用有界队列:
new ArrayBlockingQueue(100),配合合理拒绝策略 - 若需缓冲但怕 OOM,考虑
SynchronousQueue(不存储任务,直接移交线程执行,等价于“无缓冲”),此时maximumPoolSize必须大于corePoolSize才有意义 - 千万别用
Executors.newCachedThreadPool()处理不可控并发量——它的maximumPoolSize = Integer.MAX_VALUE,线程数疯涨,系统直接卡死
线程池不是配置一次就高枕无忧的东西,getActiveCount()、getQueue().size()、getCompletedTaskCount() 这三个值得定期采样,比任何文档都真实。










