BlockingQueue作为Java并发编程的核心组件,通过内置的阻塞机制完美解决了生产者-消费者模式的线程协作问题。本文将深入解析其实现原理、五种典型队列及应用场景。

java.util.Collection
└── java.util.Queue
└── java.util.concurrent.BlockingQueue(接口)
├── ArrayBlockingQueue —— 有界数组阻塞队列
├── LinkedBlockingQueue —— 可选有界链表阻塞队列
├── PriorityBlockingQueue —— 无界优先级阻塞队列
├── DelayQueue —— 延迟阻塞队列
├── SynchronousQueue —— 同步移交队列(容量为 0)
└── LinkedTransferQueue —— 链表传输队列(JDK 7+)
BlockingQueue 为每个操作提供了四种处理策略,这是理解它的核心:
| 操作类型 | 立即抛异常 | 立即返回特殊值 | 无限期阻塞 | 超时阻塞 |
|---|---|---|---|---|
| 插入 | add(e) | offer(e) | put(e) | offer(e, time, unit) |
| 移除 | remove() | poll() | take() | poll(time, unit) |
| 查看 | element() | peek() | 不支持 | 不支持 |
IllegalStateException 或 NoSuchElementExceptionput(e) 和 take() 是 BlockingQueue 区别于普通 Queue 的标志性方法,它们让生产者-消费者协调从"轮询检查"变为"事件驱动",极大简化了并发编程模型。
内部设计:底层使用一个定长数组 Object[] items,配合一把独占锁 ReentrantLock 和两个条件变量 notEmpty(消费者等待)与 notFull(生产者等待)。所有操作共享同一把锁,这意味着同一时刻只能有一个线程执行入队或出队。
关键源码片段:
// put 方法的核心逻辑
public void put(E e) throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length)
notFull.await(); // 队列满,生产者等待
enqueue(e); // 实际入队
} finally {
lock.unlock();
}
}
enqueue 和 dequeue 都是 O(1) 操作,因为 ArrayBlockingQueue 也使用了类似 ArrayDeque 的循环索引方式,元素不需要被移动。
特点:
new ArrayBlockingQueue<>(capacity, true) 使用公平锁,按等待时间长短分配锁内部设计:基于单向链表节点,使用两把锁分离入队和出队操作——putLock 控制尾部插入,takeLock 控制头部移除。这种设计使得生产者和消费者可以在一定程度上并行操作,吞吐量通常高于 ArrayBlockingQueue。
关键源码片段:
// 节点定义
static class Node {
E item;
Node next;
Node(E x) { item = x; }
}// 入队(持 putLock)
private void enqueue(Node node) {
last = last.next = node;
}// 出队(持 takeLock)
private E dequeue() {
Node h = head;
Node first = h.next;
h.next = h; // help GC
head = first;
E x = first.item;
first.item = null;
return x;
}
特点:
Integer.MAX_VALUE(相当于无界)内部设计:与 PriorityQueue 的堆实现相同,但增加了并发控制。使用一把 ReentrantLock 和一个 notEmpty 条件变量。由于是无界队列,不存在 notFull 条件——put 永远不会阻塞。
特点: