线程池任务队列溢出是系统“水位已满”信号,需搭配有界队列(如arrayblockingqueue)和适配业务的拒绝策略(如callerrunspolicy、discardpolicy等),禁用无界队列与默认abortpolicy,配合上游限流与实时监控实现可观察、可响应的过载保护。

线程池任务队列溢出不是故障,而是系统明确发出的“水位已满”信号。关键不在于避免溢出,而在于让溢出行为符合业务预期:该限流时自然减速,该丢弃时安静处理,该保底时可靠落库。
必须搭配有界队列,否则拒绝策略形同虚设
拒绝策略只在两个条件同时满足时触发:工作队列已满 且 当前线程数已达 maximumPoolSize。如果用 LinkedBlockingQueue 默认无界构造(容量为 Integer.MAX_VALUE),任务会持续入队直至堆内存耗尽,拒绝逻辑根本不会执行。
- 生产环境推荐 ArrayBlockingQueue,显式指定容量,比如 200~500,匹配业务可容忍的最大积压量
- 若需兼顾吞吐与可控性,可用带容量的 LinkedBlockingQueue(new LinkedBlockingQueue(500)),但要警惕其锁竞争开销
- 避免 SynchronousQueue 用于非高并发短任务场景——它不缓存,提交即需线程接手,“溢出”实为线程创建失败
四种内置策略各司其职,别默认用 AbortPolicy
AbortPolicy 是默认策略,抛 RejectedExecutionException。它适合测试或强一致性批处理,但在 Web 接口等场景中直接抛异常会导致接口不可用,生产环境慎用。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- CallerRunsPolicy:调用线程(如 Tomcat worker 线程)同步执行被拒任务。天然反压,拖慢提交节奏,适合日志、MQ 发送等非核心路径;但需确保调用方能承受延迟(如 Feign 超时 ≤ 800ms)
- DiscardPolicy:静默丢弃,零开销。仅适用于埋点、指标采集等允许丢失的任务,且必须配套采样开关和丢弃计数监控
- DiscardOldestPolicy:丢队首任务再重试提交当前任务。适合缓存刷新、状态同步等时效敏感场景,但注意 PriorityBlockingQueue 可能丢错优先级任务
自定义策略要轻量、异步、可观测
写自定义 RejectedExecutionHandler 最容易踩坑:拒绝逻辑本身不能成为新瓶颈。
- 拒绝方法内禁止调用 executor.submit()、queue.offer() 等可能阻塞或递归的操作,否则可能死锁
- 补偿动作(如写 DB、发 MQ)必须异步化,走独立线程池或消息队列,与拒绝路径解耦
- 至少记录任务标识、当前队列大小、线程池活跃数;用 AtomicLong 统计次数,并通过 Micrometer 暴露为 Prometheus 指标
拒绝策略只是兜底,真正流控得靠上游协同
把压力挡在线程池之外,比等它溢出再处理更有效。
- 在 API 网关或服务入口用 Sentinel 做 QPS 限流,例如 /order/create 接口限制 500 QPS
- 对下游不稳定依赖(短信、通知)配置熔断,超时或错误率超标时自动切断,防止反压传导
- 禁用 Executors.newFixedThreadPool() —— 它底层是无界 LinkedBlockingQueue,几百个慢任务就能吃掉上 GB 堆内存
拒绝策略不是用来扛流量的,它是压力报警器。配合有界队列、合理线程数、实时监控(队列使用率 >75%、拒绝数突增),才能让系统在过载时依然可观察、可响应、可恢复。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










