ArrayBlockingQueue是Java中线程安全、基于数组的有界阻塞队列,适合固定容量的生产者-消费者场景;构造时需指定大于0的capacity,支持公平锁选项;应优先使用put/take实现阻塞式存取,避免offer/poll导致丢数据或空转。

ArrayBlockingQueue 是什么,为什么选它做有界缓冲区
ArrayBlockingQueue 是 Java 并发包(java.util.concurrent)中一个线程安全、基于数组实现的有界阻塞队列。它天然适合构建固定容量的生产者-消费者缓冲区:容量在构造时就锁定,不会扩容;插入/获取操作自动处理线程阻塞与唤醒;内部使用单一 ReentrantLock 保证一致性,开销可控。
它不是万能的——如果你需要动态扩容、优先级排序或无界行为,ArrayBlockingQueue 就不合适。但只要明确“容量固定 + 多线程安全 + FIFO”这三点,它就是最直接、最轻量的选择。
初始化时必须指定容量,且不能为 0 或负数
构造 ArrayBlockingQueue 的最小必要参数只有一个:int capacity。这个值必须大于 0,否则抛出 IllegalArgumentException。
// ✅ 正确:容量为 16,公平锁(可选) ArrayBlockingQueue<string> buffer = new ArrayBlockingQueue(16, true); <p>// ❌ 错误:capacity = 0 → IllegalArgumentException new ArrayBlockingQueue<string>(0);</string></p> <p>// ❌ 错误:capacity = -1 → IllegalArgumentException new ArrayBlockingQueue<string>(-1);</string></p></string>
注意:
- 容量一旦设定,无法修改,也没有
resize()方法 - 第二个布尔参数
fair控制锁是否公平,默认false;设为true可避免线程饥饿,但吞吐量略低 - 不要试图用
new ArrayBlockingQueue(Integer.MAX_VALUE)模拟“无界”,这会直接 OOM
put() 和 take() 是阻塞式核心操作,别混用 offer()/poll()
生产者应优先用 put(E e),消费者用 take()。它们会在队列满/空时自动阻塞当前线程,直到条件满足——这是构建稳定缓冲区的关键语义。
// 生产者线程
buffer.put("item-1"); // 队列满则阻塞,直到有空间
<p>// 消费者线程
String item = buffer.take(); // 队列空则阻塞,直到有元素</p>
而 offer(E e) 和 poll() 是非阻塞的:
-
offer()在满时返回false,不阻塞也不抛异常 -
poll()在空时返回null,同样不阻塞
容易踩的坑:
- 在高并发写入场景下,用
offer()而不处理返回false的情况,会导致数据静默丢失 - 用
poll()循环轮询空队列,白白消耗 CPU(应改用take()或带超时的poll(long timeout, TimeUnit unit))
注意中断响应和 close 语义缺失
ArrayBlockingQueue 不提供 close() 或 shutdown() 方法。它的生命周期完全由引用管理。如果想“停用”缓冲区,常见做法是:
- 向队列插入特殊结束标记(如
null或自定义 sentinel 对象) - 或让生产者调用
interrupt()中断正在put()的线程,捕获InterruptedException后退出
try {
buffer.put(item);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 恢复中断状态
return; // 退出生产逻辑
}
关键点:
- 所有阻塞方法(
put、take、poll(timeout))都响应中断 - 不要忽略
InterruptedException,也不要简单吞掉它 - 没有“清空并拒绝新任务”的原子操作,需自行协调生产者/消费者退出顺序
实际用的时候,边界条件比想象中多:容量初始化错、混用阻塞/非阻塞 API、忽略中断、没处理关闭信号——这些都会让缓冲区行为变得难以预测。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











