必须用有界队列替代伪无界队列,显式设置合理容量(如1000),配合callerrunspolicy或自定义拒绝策略,并监控队列水位、源头限流。

Java 线程池使用无界队列(如 LinkedBlockingQueue)时,默认构造的队列容量为 Integer.MAX_VALUE,表面“无界”,实则极易因任务持续堆积导致堆内存耗尽(OutOfMemoryError)。此时,**拒绝策略根本不会触发**——因为队列永远不会满,线程池永远不拒绝任务。所以问题本质不是“如何配置拒绝策略”,而是:**必须打破“无界”假象,主动引入容量约束与可控拒绝机制**。
用真正有界的队列替代“伪无界”队列
不要依赖 new LinkedBlockingQueue() 的默认构造。显式指定合理容量,让队列可满、拒绝策略可生效:
- 例如:
new LinkedBlockingQueue(1000),把“无界”变成明确上限 - 容量需结合业务吞吐、单任务内存开销、JVM堆大小估算(如堆 2GB,单任务平均占 1MB,则队列不宜超 1000)
- 避免设为
Integer.MAX_VALUE或过大的常量(如 100_0000),这仍是隐性风险
配合有界队列,选用并定制合理的拒绝策略
当队列满 + 核心线程忙 + 最大线程已达上限时,拒绝策略才真正起作用。推荐组合:
Java JDK 25 来自 OpenJDK 官方归档,版本为 JDK 25,本条下载地址已指向官方 Windows x64 zip 安装包直链,适合调试旧项目或兼容旧版 Java 运行环境。
- CallerRunsPolicy:让调用线程自己执行任务,天然限流(降低提交速率),适合对延迟敏感但能接受慢提交的场景
-
自定义拒绝策略:在
RejectedExecutionHandler中记录告警日志、触发熔断(如降级开关)、甚至抛出带上下文的运行时异常(如new RejectedTaskException("queue full, reject at " + System.currentTimeMillis())) - 避免使用
AbortPolicy(直接抛异常)或DiscardPolicy(静默丢弃),除非你明确接受丢失且已做好补偿
加一层防御性监控与自动干预
仅靠拒绝策略被动拦截不够,需主动观测和干预:
- 定期采集线程池指标:
getQueue().size()、getActiveCount()、getTaskCount(),当队列使用率 > 80% 持续 30 秒,触发告警 - 集成 Micrometer / Prometheus,暴露
thread_pool_queue_size等指标,接入 Grafana 做可视化水位看板 - 关键服务可结合 Sentinel 或 Resilience4j,在队列积压达到阈值时自动触发 fallback 或限流降级
从源头控制任务提交节奏
拒绝策略是最后一道防线,更有效的防御在上游:
- 提交任务前做简单预检:
if (executor.getQueue().size() > MAX_QUEUE_THRESHOLD) throw new TaskRejectedException(...) - 使用异步非阻塞方式提交(如封装成 CompletableFuture),配合 timeout 控制等待时间,避免调用线程长期阻塞
- 对批量任务做分片+节流,例如每秒最多提交 50 个,用
ScheduledExecutorService或令牌桶控制速率
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










