BlockingDeque 是支持双端阻塞操作的线程安全队列,适合两端可进出、容量可控、优先级调度的生产者-消费者场景;它继承 BlockingQueue,扩展头尾插入/移除及超时阻塞方法,可用 LinkedBlockingDeque 限定容量模拟数组级语义。
BlockingDeque 是 Java 并发包(java.util.concurrent)中支持双端阻塞操作的线程安全队列,它天然适合实现“两端可进出 + 阻塞等待”的生产者-消费者模型,尤其适用于需要**数组级容量控制、优先级调度(如栈式消费)、或双向任务分发**的场景。
为什么选 BlockingDeque 而不是 BlockingQueue?
BlockingDeque 继承自 BlockingQueue,但额外支持在头尾两端插入/移除元素,并提供带超时和无限期阻塞的双端方法(如
,
,
)。这意味着:
你可以让生产者统一从尾部(
/
)入队,消费者从头部(
)取,模拟 FIFO 数组行为;
也可以让高优先级任务“插队”到头部(
),低优先级任务入尾部,实现轻量级优先级队列;
队列底层可基于数组(
—— 注意:JDK 本身未提供该类,但可用
+ 容量限制模拟数组级固定容量语义);
所有操作自动阻塞等待空间或元素,无需手动加锁或条件变量。
用 LinkedBlockingDeque 模拟“数组级”容量控制
JDK 没有内置的
,但
支持构造时指定容量上限,从而获得与数组队列一致的**有界性、满/空阻塞、公平性保障**:
此时:
在满时阻塞,直到有空间;
在空时阻塞,直到有元素;
容量不可动态扩容,符合“数组级”的硬边界语义;
虽底层是链表节点,但因容量受限且无 GC 压力突增,实际性能接近紧凑数组队列。
典型双端生产者-消费者流程代码结构
以下是一个带日志、固定容量、头取尾入(FIFO)、支持优雅关闭的示例:
关键注意事项与增强技巧
使用 BlockingDeque 实现稳定流程控制,需注意:
避免混用首尾操作逻辑
:若约定“生产→尾,消费→首”,就不要在消费者里调
,否则破坏 FIFO;一致性靠编码规范+封装(如包装成
类);
中断响应必须处理
:所有
/
方法在被中断时抛
,务必恢复中断状态并退出循环;
超时替代无限阻塞更健壮
:用
和
可防止单点卡死,配合重试或降级;
容量设为 2 的幂次非必需
:不同于
(内部用循环数组),
容量只是计数器,设 100、512 或 1024 效果相同;
监控队列水位可辅助流控
:调用
或
获取实时负载,触发告警或动态限速。
不复杂但容易忽略:BlockingDeque 的真正价值不在“双端”,而在于把容量控制、线程阻塞、中断传播全部封装进一个对象,让生产者消费者只需关注业务逻辑本身。
putFirst()takeLast()pollFirst(5, TimeUnit.SECONDS)addLast()putLast()takeFirst()addFirst()ArrayBlockingDequeLinkedBlockingDequeArrayBlockingDequeLinkedBlockingDequeBlockingDeque deque = new LinkedBlockingDeque<>(1024); // 固定容量 1024 putFirst()/putLast()takeFirst()/takeLast()// 创建有界双端阻塞队列
BlockingDeque taskDeque = new LinkedBlockingDeque<>(100);
// 生产者线程(尾部写入)
new Thread(() -> {
for (int i = 0; i < 1000; i++) {
Task task = new Task("job-" + i);
try {
taskDeque.putLast(task); // 满则阻塞
System.out.println("Produced: " + task.id);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}).start();
// 消费者线程(头部读取)
new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
try {
Task task = taskDeque.takeFirst(); // 空则阻塞
System.out.println("Consumed: " + task.id);
Thread.sleep(10); // 模拟处理耗时
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break;
}
}
}).start(); takeLast()FifoTaskQueueputXxx()takeXxx()InterruptedExceptionofferLast(task, 3, TimeUnit.SECONDS)pollFirst(3, TimeUnit.SECONDS)ArrayBlockingQueueLinkedBlockingDequedeque.size()deque.remainingCapacity()