线程池根据corepoolsize、workqueue和maximumpoolsize三者配合决定新建线程、排队或拒绝:先尝试用核心线程执行,满则入队,队满且线程数未达maximumpoolsize才新建线程,否则拒绝。

线程池怎么决定该新建线程、排队还是直接拒绝?
关键看 corePoolSize、workQueue 和 maximumPoolSize 三者如何配合,不是“先排队再扩容”这么简单。
- 提交任务时,若当前线程数 corePoolSize,**立刻新建核心线程执行**(哪怕已有空闲线程)
- 若线程数 ≥
corePoolSize,且workQueue未满,任务**入队等待**,不新建线程 - 若队列已满,且当前线程数 maximumPoolSize,才新建**非核心线程**执行任务
- 若队列满 + 线程数已达
maximumPoolSize,触发handler拒绝策略(默认抛RejectedExecutionException)
常见错误:用 LinkedBlockingQueue 无界队列(如 new LinkedBlockingQueue()),导致永远不扩容、永远不拒绝——流量突增时任务全堆在内存里,OOM 风险极高。
keepAliveTime 对谁生效?很多人设了却没效果
keepAliveTime **只约束非核心线程**,即超过 corePoolSize 的那部分线程。核心线程默认永驻,除非显式调用 allowCoreThreadTimeOut(true)。
- 场景举例:设
corePoolSize=2、maximumPoolSize=10、keepAliveTime=60、unit=TimeUnit.SECONDS→ 高峰期创建了 8 个非核心线程,峰值过后,这 8 个线程会在空闲 60 秒后陆续销毁,最终只剩 2 个核心线程 - 如果忘了配
unit(比如传60却没指定是秒还是毫秒),线程可能秒退或死扛几分钟,行为完全不可控 - 用
ArrayBlockingQueue等有界队列时,keepAliveTime才真正起作用;无界队列下,非核心线程几乎不会被创建,自然也谈不上超时销毁
为什么阿里规约禁止用 Executors 快捷方法?
因为 Executors.newFixedThreadPool(n)、newCachedThreadPool() 等内部硬编码了危险参数,掩盖了真实风险。
立即学习“Java免费学习笔记(深入)”;
-
newFixedThreadPool(5)→ 底层用的是LinkedBlockingQueue无界队列,等同于「不限制排队长度」 -
newCachedThreadPool()→corePoolSize=0、maximumPoolSize=Integer.MAX_VALUE、SynchronousQueue,意味着来一个任务就开一个线程,瞬间打爆线程数 - 它们还用了默认
ThreadFactory,线程名全是pool-X-thread-Y,线上出问题根本无法定位是哪个模块提交的任务
正确做法:一律用 ThreadPoolExecutor 构造器显式传参,至少明确控制 workQueue 容量和 handler 行为。
自定义 threadFactory 和 handler 是不是可选项?
不是。生产环境必须配,否则等于裸奔。
-
threadFactory至少要给线程命名,例如"order-processor-pool-%d",方便 GC 日志、jstack、Arthas 中快速归因 -
handler切忌用默认的AbortPolicy(抛异常中断流程)。更稳妥的是:CallerRunsPolicy(让提交线程自己执行,天然限流)、或包装成打监控+降级日志的自定义策略 - 漏配
threadFactory导致所有线程共用同一个名字前缀,jstack 里几百个pool-1-thread-1,你分不清哪个是支付线程池、哪个是通知线程池
线程池不是配置完就能高枕无忧的组件,workQueue 容量、handler 行为、线程命名这三项,任何一个没对齐业务流量特征和可观测需求,都会让故障排查成本翻几倍。










