线程池队列满了会怎样?从拒绝策略看懂 ThreadPoolExecutor 的扩容顺序

线程池队列满了会怎样?

面试里问"线程池队列满了怎么办",别急着背四个拒绝策略。

拒绝不是队列一满就发生。对一个手动创建的 ThreadPoolExecutor,它会先尝试补到核心线程数;核心线程忙完后,才优先把任务放进队列;队列塞不下,才继续创建非核心线程;线程已经到最大值,才交给拒绝处理器。

这几个参数放在一起看,顺序就很直观:

java 复制代码
ThreadPoolExecutor pool = new ThreadPoolExecutor(
    2,
    4,
    30,
    TimeUnit.SECONDS,
    new ArrayBlockingQueue<>(2),
    new ThreadPoolExecutor.AbortPolicy()
);

假设前几个任务都还没跑完。

  • 第 1、2 个任务:直接创建核心线程执行。
  • 第 3、4 个任务:核心线程已满,进入容量为 2 的队列。
  • 第 5、6 个任务:队列满了,继续创建线程,最多扩到 4 个。
  • 第 7 个任务:线程数和队列都到上限,才会触发 AbortPolicy,抛出 RejectedExecutionException。

很多人答到这里会说"把队列调大就行"。这通常只是把超载藏起来。

无界队列会让 maximumPoolSize 基本失去作用:核心线程都忙时,后续任务一直排队,不会因为积压而扩到最大线程数。积压足够久,问题会从"被明确拒绝"变成"接口一直没返回,内存还在涨"。

有界队列不是为了制造拒绝,而是把延迟预算写进配置。比如单个任务平均 100 毫秒,4 个工作线程在稳定状态下每秒最多处理约 40 个任务。队列容量 200 意味着只看排队就可能多等约 5 秒。这个等待是否能接受,要由调用方的超时和业务时效决定。

四种默认拒绝策略,别按名字选

AbortPolicy 是默认值。它会抛异常,适合不能悄悄丢的任务。调用点要把它转换成可观测的过载信号,例如错误日志、指标和明确的失败响应。

CallerRunsPolicy 让提交任务的线程自己执行任务。它确实能给上游施加背压,但不要把它随手放进所有 Web 请求链路。调用线程如果是事件循环、持有锁,或者本身不能被长时间占用,背压会变成新的卡顿。

DiscardPolicy 会静默丢任务。除非业务天然允许丢弃,而且有丢弃计数和补偿手段,否则它最危险。日志里什么都没有,任务却已经没了。

DiscardOldestPolicy 会丢掉队列头部最早的任务,再重试提交当前任务。只在"新状态一定比旧状态更有价值"的场景才有讨论空间,例如可合并的最新状态刷新。对订单、通知、异步写库这类 FIFO 语义敏感的任务,不该用它赌运气。

生产里我更常把"拒绝"当成容量告警,而不是自动重试的起点。重试还是投回同一个已经饱和的池子,只会让队列更难退下来。

参数该从什么开始定?

先别从"CPU 核数乘几"起步,先把任务分开。

计算密集型任务,线程太多会把时间花在线程切换上;阻塞型任务可以容纳更多并发,但它占住的往往是数据库连接、下游 HTTP 配额或文件句柄。线程池最大值必须同时受这些下游资源约束。

然后拿高峰提交速率、单任务耗时、可接受排队时长推一个初始容量,再压测。观察至少三组数:活跃线程数、队列长度、拒绝次数。只有队列长度,没有拒绝次数,往往看不出"已经开始掉任务"还是"只是慢了一点"。

JDK 的 ThreadPoolExecutor 文档把这件事说得很明确:有限的最大线程数和有限队列同时耗尽时,任务才会被拒绝。面试里把这条路径讲清,再结合你的业务说为什么选有界队列和哪一种拒绝策略,已经比背参数表强很多。

相关推荐
白山编程大哥几秒前
Java 后端如何优雅地把 List 集合返回给前端
java·前端·list
Linux运维技术栈12 分钟前
CVE-2026-91403 高危漏洞修复:Rocky Linux 9 多版本 JDK 环境清理实操
java·linux·运维
On The Road :13 分钟前
Java 多线程详解:从入门到线程安全
java·python·安全
Shaoshing25 分钟前
LeetCode100 49. 字母异位词分组
java·算法
划破黑暗的第一缕曙光1 小时前
[C++]:5.STL:string(2)
java·c++·redis
hhhjhl2 小时前
Undici 跨端三方库仓颉适配全流程
java·开发语言·ubuntu
wangchunyu1142 小时前
JAVA JVM内存结构与运行时数据区
java·开发语言·jvm
光锥智能2 小时前
海康机器人举行2026移动机器人新品发布会:箱式存拣升级,「明智」系列发布
java·前端·机器人
行思理2 小时前
Nginx 跨站请求(CORS)配置完全指南:从原理到生产实践
java·前端·nginx
小溪学编程3 小时前
Spring @Bean 与 @Component 注解的区别详解
java·后端·spring