corePoolSize 和 maximumPoolSize 应据任务类型设定:CPU 密集型≈CPU 核数,IO 密集型为 2–4 倍;corePoolSize=0 仅适用于 SynchronousQueue,LinkedBlockingQueue 下 maximumPoolSize 无效;keepAliveTime 仅作用于非核心线程,需配合 allowCoreThreadTimeOut(true) 才影响核心线程;拒绝策略优先选 CallerRunsPolicy 或自定义可观测策略;workQueue 推荐有界 ArrayBlockingQueue 而非无界 LinkedBlockingQueue,避免 OOM。

corePoolSize 和 maximumPoolSize 怎么设才不翻车
这两个参数直接决定线程池的伸缩边界。设得太小,任务排队堆积;设得太大,又浪费资源甚至触发 OutOfMemoryError。关键看任务类型:CPU 密集型建议设为 Runtime.getRuntime().availableProcessors() 或略高(比如 +1);IO 密集型则可设为 2–4 倍 CPU 核数,因为线程常在等待响应。
常见错误是把 corePoolSize 设为 0,以为“按需创建”,结果在低负载时连第一个任务都得等 keepAliveTime 超时后才创建线程——这会导致首请求延迟突增。另外,maximumPoolSize 在使用 LinkedBlockingQueue 时完全无效(队列无界,永远不扩容),这点极易被忽略。
-
corePoolSize == 0:仅适用于SynchronousQueue这类直接移交队列 -
corePoolSize == maximumPoolSize:等效于固定大小线程池,避免动态伸缩开销 - 若用
ArrayBlockingQueue,必须确保maximumPoolSize > corePoolSize才可能触发扩容
keepAliveTime 为什么有时根本不起作用
这个参数只对超出 corePoolSize 的空闲线程生效。如果线程池始终没超过核心线程数,那它就纯属摆设。更隐蔽的问题是:JDK 6–8 中,即使设置了 allowCoreThreadTimeOut(true),keepAliveTime 对核心线程也只在 prestartAllCoreThreads() 之后创建的线程上生效——不是所有核心线程都受控。
实际调优中,短生命周期任务(如 HTTP 短连接处理)适合设较小值(比如 10–60 秒),长任务则建议保持默认 60 秒或更大,避免频繁销毁重建线程的开销。
立即学习“Java免费学习笔记(深入)”;
- 未调用
allowCoreThreadTimeOut(true)时,核心线程永不超时退出 -
keepAliveTime单位必须和传入的TimeUnit严格匹配,单位错会导致超时时间偏差百倍 - 在 Spring 的
@Async场景下,若复用同一ThreadPoolTaskExecutor,该值会影响所有异步方法的线程存活行为
拒绝策略(RejectedExecutionHandler)选哪个、怎么自定义
默认的 AbortPolicy 直接抛 RejectedExecutionException,线上服务往往不能接受这种“甩锅”行为。更稳妥的是 CallerRunsPolicy:让提交任务的线程自己执行,天然限流,且不丢失任务。但要注意——如果调用方是 Web 请求线程,等于把压力反压回 Tomcat 线程池,可能造成请求吞吐骤降。
自定义策略时,重点不是“记录日志”,而是“做决策”。比如写入 Kafka 重试队列、降级为同步执行、或触发告警并采样 dump 当前线程池状态(getActiveCount()、getQueue().size())。
-
DiscardPolicy:静默丢弃,适合允许丢失的离线任务 -
DiscardOldestPolicy:丢队头,慎用于有顺序依赖的任务 - 自定义实现必须注意线程安全:拒绝发生在任务提交线程上下文,不是线程池内部线程
workQueue 用 ArrayBlockingQueue 还是 LinkedBlockingQueue
表面看 LinkedBlockingQueue 无界、省心,但它会掩盖容量规划问题——任务持续积压时,内存不断增长,直到 OOM。而 ArrayBlockingQueue 是有界的,配合合理的拒绝策略,能及早暴露系统瓶颈。
一个典型误用:用 new LinkedBlockingQueue()(无参构造),它默认容量是 Integer.MAX_VALUE,几乎等价于无限队列。真要“大容量”,应显式指定合理上限,比如 new LinkedBlockingQueue(1000)。
-
SynchronousQueue不存储任务,适合追求极致响应、且任务提交速率与执行速率基本匹配的场景 -
PriorityBlockingQueue需自行实现Comparable,但线程池不保证按优先级调度(仅插入时排序,取任务仍按 FIFO) - Spring Boot 2.3+ 默认
ThreadPoolTaskExecutor使用LinkedBlockingQueue,上线前务必检查是否覆盖了 capacity
线程池不是配置完就一劳永逸的东西。最常出问题的,其实是 workQueue 容量和 maximumPoolSize 的组合是否匹配,以及拒绝策略有没有真正落地成可观测、可干预的动作。光看参数名,看不出业务水位是否真实可控。










