应优先选用有界队列(如arrayblockingqueue或带容量的linkedblockingqueue)以避免oom,禁用无界队列;容量需按峰值qps×平均rt×安全系数估算,并配合适当拒绝策略与实时监控。

选有界队列是避免 OOM 的基本前提,无界队列(尤其是默认构造的 LinkedBlockingQueue)在高负载下极易导致任务无限堆积、堆内存持续上涨,最终触发 OOM。关键不在“能不能用”,而在“是否可控”。
优先用有界队列,明确容量上限
有界队列强制设限,把最大积压量锁死在可预期范围内。例如:
-
ArrayBlockingQueue:推荐首选,构造时必须指定容量(如
new ArrayBlockingQueue(200)),内存连续、缓存友好,适合资源敏感型场景 -
带容量的 LinkedBlockingQueue:如
new LinkedBlockingQueue(256),双锁机制吞吐略高,但需注意链表节点额外内存开销 - 容量值不能拍脑袋定:建议按 峰值 QPS × 平均 RT(秒)× 安全系数 1.2~1.5 初步估算;若任务对象较大(如含 Base64 图片),还需按 可用堆内存 × 0.7 ÷ 单任务平均大小 反推校准
彻底避开无界队列陷阱
默认的 LinkedBlockingQueue() 实际容量为 Integer.MAX_VALUE,表面宽松,实则危险:
- 任务提交快、处理慢 → 队列持续扩容 → 堆内存耗尽 → Full GC 频发 → OOM
- 线程池永远不认为“入队失败”,因此不会新建非核心线程,
maximumPoolSize形同虚设 - 上游完全感知不到压力,缺乏背压信号,容易引发雪崩
慎用 SynchronousQueue,别让它放大风险
SynchronousQueue 容量为 0,不存储任务,靠线程间直接移交:
- 内存压力最小,延迟最低,但要求线程池能立即调度空闲线程
- 若
corePoolSize过小或突发流量大,新任务会立刻触发拒绝策略 - 不适合单独用于大促或秒杀场景——它不缓冲,只倒逼线程扩容,可能造成线程数飙升、CPU 和栈内存承压
配套拒绝策略与监控不可少
有界队列只是第一道闸门,还必须配好“守门人”:
- 拒绝策略别用静默丢弃的
DiscardPolicy,推荐CallerRunsPolicy(让调用线程自己执行)或自定义策略(记录日志、触发降级、上报告警) - 实时监控
queue.size()使用率,超过 80% 就该告警;结合getActiveCount()和任务完成率,判断是否需动态调参 - 拒绝不是失败,而是系统在说“我现在扛不住”,要让这个信号快速传导到上游限流、重试或熔断模块
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











