ArrayBlockingQueue 是线程安全、有界、基于数组的阻塞队列,核心在于容量控制、方法选择、拒绝处理与任务自治。需按峰值流量×容忍延迟÷消费速率计算合理容量;优先用 offer() 配失败处理;将拒绝作为压力信号监控告警;任务应自带时效、重试、优先级等元数据实现自我管理。

ArrayBlockingQueue 是 Java 中一个线程安全、有界、基于数组实现的阻塞队列。它天然具备容量限制,是预防任务积压导致内存溢出(OOM)的关键工具。用好它,核心不在“怎么放”,而在“怎么控”——控制生产节奏、明确拒绝策略、合理设置容量,并配合任务特征做动态适配。
明确容量设定:别拍脑袋,要算任务峰值和处理延迟
ArrayBlockingQueue 的构造函数必须指定容量,这是它防 OOM 的第一道防线。但容量不是越大越好,也不是越小越稳:
- 容量太小 → 频繁触发拒绝策略,任务丢失或线程阻塞,吞吐骤降
- 容量太大 → 表面流畅,实则掩盖消费滞后问题,一旦下游卡顿,内存持续上涨,OOM 风险陡增
- 合理容量 ≈ 单位时间最大待处理任务数 × 最大可容忍等待时长 ÷ 消费速率
例如:上游每秒产生 200 个任务,消费者平均 50ms 处理一个(即每秒最多处理 20 个),若允许任务最长等待 2 秒,则理论积压上限为 200 × 2 = 400;再叠加 20% 缓冲,设为 480 容量较稳妥。实际中建议从 256 或 512 起步,结合监控(如队列使用率 >80% 持续 1 分钟)动态调优。
选用合适的 put() / offer():区分“必须进队”和“可丢弃”的语义
面对满队列,ArrayBlockingQueue 提供不同行为的方法,选错会破坏稳定性:
- put(E e):阻塞直到有空位。适合强一致性场景(如金融交易指令),但若消费者长期卡死,所有生产者线程将挂起,可能引发雪崩
- offer(E e):立即返回 true/false。适合高吞吐、可容忍少量丢失的场景(如日志采集、埋点上报)
- offer(E e, long timeout, TimeUnit unit):带超时的非阻塞,折中方案。超时后可降级(如写本地磁盘、发告警)
推荐默认用 offer() + 显式失败处理。例如:
if (!queue.offer(task)) {log.warn("Task rejected: queue full, current size={}", queue.size());
metrics.counter("task_rejected").increment();
alertIfCritical(); // 触发容量告警
}
配合拒绝策略:把“队满”变成可观测、可干预的信号
ArrayBlockingQueue 本身不内置拒绝策略(那是 ThreadPoolExecutor 的事),但它让拒绝变得明确可捕获。关键在于把“offer 失败”作为系统压力指标来用:
- 记录拒绝次数与频率,接入 Prometheus/Grafana 做实时看板
- 当 1 分钟内拒绝率 >5%,自动触发扩容动作(如增加消费者线程数)或限流(如对上游 HTTP 请求返回 429)
- 对关键任务,拒绝时保存到 Redis 或本地文件,由补偿线程异步重投
不要把拒绝当成异常忽略,它是系统在喊“我快撑不住了”。一次拒绝是信号,持续拒绝就是故障前兆。
避免变量任务积压:用任务结构体自带时效与优先级
单纯靠队列容量不够,还需任务自身“懂分寸”。建议任务对象封装必要元数据:
- 创建时间戳:消费者取任务时判断是否超时(如 >30s 直接丢弃或走死信)
- 重试次数:防止异常任务反复入队形成死循环积压
- 业务优先级字段:配合 PriorityBlockingQueue(注意:它不是有界队列)或自定义分队列(如 high/normal/low 三个 ArrayBlockingQueue)
例如定义任务类:
public class Task implements Runnable {private final long createTime = System.currentTimeMillis();
private final int priority;
private final int retryCount;
// ...
public boolean isExpired() { return System.currentTimeMillis() - createTime > 30_000; }
}
消费者在执行前先校验,让积压任务“自然失效”,而非无限堆积。

















