corepoolsize和maximumpoolsize应差异化设置以启用弹性伸缩:io密集型任务设为2×cpu核数,cpu密集型设为cpu核数+1;keepalivetime仅作用于超corepoolsize的线程,建议30–60秒;队列选型需匹配场景,避免无界队列oom;拒绝策略重在监控而非兜底。

corePoolSize 和 maximumPoolSize 到底怎么设
这两个参数决定线程池的“弹性边界”:低于 corePoolSize 的线程会一直存活(即使空闲),超过 corePoolSize 但未达 maximumPoolSize 的线程,在空闲超时后会被回收。
常见误设是把两者设成一样——等于关闭了弹性伸缩能力,突发流量只能进队列或触发拒绝策略;设得过大又会导致资源争抢、GC压力上升。
- IO 密集型任务(如 HTTP 调用、DB 查询):可设为
2 × CPU核心数或略高,因线程常阻塞,多些线程能提升吞吐 - CPU 密集型任务(如图像处理、复杂计算):通常设为
CPU核心数 + 1,避免上下文切换开销 - 混合型任务建议先压测,观察线程利用率和平均响应时间拐点,再反推合理范围
keepAliveTime 对非核心线程的实际影响
keepAliveTime 只作用于 > corePoolSize 的线程,且仅在线程池中线程数 > corePoolSize 时才开始计时。很多人以为它控制所有空闲线程,其实不是。
默认单位是秒,但容易被忽略的是:如果用 TimeUnit.MILLISECONDS 却传入 60,实际只保留 60 毫秒——线程刚建好就销毁,反复创建反而拖慢性能。
立即学习“Java免费学习笔记(深入)”;
- 生产环境建议设为 30–60 秒,平衡资源复用与及时释放
- 若使用
SynchronousQueue(无缓冲队列),keepAliveTime几乎不起作用,因为任务不会排队,线程池会频繁扩缩 - 调用
allowCoreThreadTimeOut(true)后,corePoolSize内的线程也会受此参数约束
BlockingQueue 的选型直接决定拒绝行为
线程池是否丢任务、卡主线程、OOM,很大程度取决于队列类型:
-
LinkedBlockingQueue(无界):看似安全,实则可能吃光堆内存,尤其当任务提交速率持续高于执行速率时 -
ArrayBlockingQueue(有界):必须配合合理的容量和拒绝策略,否则容易快速触发RejectedExecutionException -
SynchronousQueue:不存储任务,要求有空闲线程立刻承接,适合高响应要求场景,但对峰值容忍度低 -
DelayedWorkQueue(仅 ScheduledThreadPoolExecutor 使用):不适用于普通线程池
别直接 new LinkedBlockingQueue()——它默认容量是 Integer.MAX_VALUE,等于事实上的无界队列。
拒绝策略(RejectedExecutionHandler)不是兜底,而是信号灯
四种内置策略里,AbortPolicy(抛异常)和 CallerRunsPolicy(由提交线程自己执行)最常用,但它们的意义完全不同:
-
AbortPolicy是明确告诉你“系统已过载”,应由上游限流或降级来应对,而不是在 catch 里静默吞掉异常 -
CallerRunsPolicy表面看是“不丢任务”,但会让业务线程阻塞,可能引发雪崩——比如 Web 请求线程池满了,HTTP 线程开始执行业务逻辑,导致整个接口变慢甚至超时 - 自定义策略务必避免耗时操作(如写 DB、发 MQ),否则会卡住线程池的拒绝判断流程
真正关键的不是选哪个策略,而是监控 rejectedExecutionCount ——只要这个值非零,说明配置或流量模型已经出问题了。









