public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> {}
继承了阻塞队列BlockingQueue 和 双端队列Deque,所以它结合了双端队列(Deque)和阻塞队列(BlockingQueue)的特性
public interface BlockingQueue<E> extends Queue<E> {}
public interface Deque<E> extends Queue<E> {}
1.线程安全
所有操作都是原子性的,支持多线程并发访问
2.阻塞操作
当队列满时:插入操作会阻塞线程,直到空间可用
当队列空时:移除操作会阻塞线程,直到元素出现
3.双端操作
支持在队列的头部和尾部进行插入/移除操作
4.超时控制
提供带超时参数的 offer 和 poll 方法(避免无限期阻塞)
5.容量限制
可以是有界(固定容量)或无界(默认 Integer.MAX_VALUE)
BlockingDeque 在 Deque 基础上扩展了阻塞方法
操作 | 头部 (First) | 尾部 (Last) |
---|---|---|
阻塞插入 | putFirst(e) |
putLast(e) |
阻塞移除 | takeFirst() |
takeLast() |
超时插入 | offerFirst(e, timeout, unit) |
offerLast(e, timeout, unit) |
超时移除 | pollFirst(timeout, unit) |
pollLast(timeout, unit) |
继承自 Deque | addFirst(e) , removeFirst() 等 |
addLast(e) , removeLast() 等 |
继承自 Queue | put(e) (等价于 putLast(e) ) |
take() (等价于 takeFirst() ) |
注意:
putXxx()
在队列满时无限阻塞takeXxx()
在队列空时无限阻塞offerXxx(timeout)
和pollXxx(timeout)
在超时后返回false
/null
LinkedBlockingDeque
是 Java 标准库中 BlockingDeque
的唯一实现类
public class LinkedBlockingDeque<E>
extends AbstractQueue<E>
implements BlockingDeque<E>, java.io.Serializable {}
继承自AbstractQueue 实现 BlockingDeque 而
public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E> {}
public interface BlockingQueue<E> extends Queue<E> {}
public interface Deque<E> extends Queue<E> {}
衍生进程 :
基本队列 衍生出---》 双端队列 和阻塞基本队列(安全)
双端队列 和阻塞基本队列(安全) ---》衍生出 阻塞双端队列BlockingDeque
BlockingDeque 唯一实现类 LinkedBlockingDeque
1.无参构造方法
创建一个容量为Integer.MAX_VALUE的LinkedBlockingDeque
public LinkedBlockingDeque() {this(Integer.MAX_VALUE);}
2.指定容量的构造方法
创建一个具有给定容量的LinkedBlockingDeque
public LinkedBlockingDeque(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
}
3.从集合构造
创建一个包含指定集合元素的LinkedBlockingDeque,容量为Integer.MAX_VALUE,并按照集合迭代器的顺序添加元素
public LinkedBlockingDeque(Collection<? extends E> c) {
this(Integer.MAX_VALUE);
// 将集合中的元素逐个添加到队列中
// 如果添加过程中超过了容量,会抛出IllegalStateException
//因为容量是MAX_VALUE,所以几乎不会发生,除非集合元素数量超过MAX_VALUE
for (E e : c)
add(e);
}
另外需要注意:
如果集合c为null,会抛出NullPointerException;如果集合c中包含null元素,也会抛出NullPointerException(因为LinkedBlockingDeque不允许null元素)
重要点
1.无界队列(无参构造)可能会因为元素过多而导致内存溢出,所以使用时要注意。
2.有界队列(指定容量)在队列满时,再添加元素的操作会被阻塞(如果使用阻塞方法如put),或者返回特殊值(如offer返回false)。
3.不允许null元素 不允许包含null的集合,否则会抛出NullPointerException
4.迭代器是弱一致性的,即迭代器创建后,队列的修改可能不会在迭代器中反映出来,或者只反映部分修改
Node节点
static final class Node<E> {
E item; // 存储元素
Node<E> prev; // 前驱节点指针
Node<E> next; // 后继节点指针
Node(E x) {
item = x;
}
}
关键成员变量
transient Node<E> first; // 队首节点
transient Node<E> last; // 队尾节点
private final int capacity; // 队列容量(无界时为 Integer.MAX_VALUE)
private final AtomicInteger count = new AtomicInteger(); // 当前元素数量
private final ReentrantLock lock = new ReentrantLock(); // 全局锁
private final Condition notEmpty = lock.newCondition(); // 非空条件
private final Condition notFull = lock.newCondition(); // 非满条件
并发控制逻辑
1. 锁机制
ReentrantLock
。notEmpty
:队列空时阻塞消费者线程(take/poll
)。notFull
:队列满时阻塞生产者线程(put/offer
)。2. 阻塞/唤醒流程
插入元素(如 putLast(e)
)
1. 获取锁
2. 若队列满 → 阻塞在 notFull 条件
3. 插入成功后:- 若原队列空 → 唤醒 notEmpty 上的消费者
4. 释放锁
移除元素(如 takeFirst()
)
1. 获取锁
2. 若队列空 → 阻塞在 notEmpty 条件
3. 移除成功后:- 若原队列满 → 唤醒 notFull 上的生产者
4. 释放锁
唤醒机制流程图
1.双端操作能力
addFirst()
/removeFirst()
)和队尾(addLast()
/removeLast()
)的插入/移除操作offerFirst()
, pollLast()
, peekFirst()
等 deque.putFirst("紧急任务"); // 优先处理高优先级任务
deque.takeLast(); // 处理普通任务
2.线程安全与并发控制
机制 | 实现方式 | 效果 |
---|---|---|
全局锁 | 单把 ReentrantLock |
简化死锁避免,保证操作原子性 |
分离条件变量 | notEmpty + notFull 双 Condition |
精准唤醒生产者/消费者线程 |
原子计数器 | AtomicInteger count |
线程安全的元素计数(O(1)复杂度) |
锁公平性:默认非公平锁(更高吞吐),可通过构造参数启用公平锁
new LinkedBlockingDeque<>(100, true); // 公平锁模式
3.阻塞行为
阻塞策略:
方法类型 | 队列满时 | 队列空时 |
---|---|---|
阻塞型 | put(e) 阻塞 |
take() 阻塞 |
超时型 | offer(e, timeout) |
poll(timeout) |
立即返回型 | offer(e) 返回false |
poll() 返回null |
异常型 | add(e) 抛异常 |
remove() 抛异常 |
唤醒机制:
4.容量灵活性
三种容量模式:
1.无界队列(默认):Integer.MAX_VALUE
2.固定有界:构造时指定容量(如 new LinkedBlockingDeque<>(200)
)
3.动态有界:通过继承实现动态扩容(需重写相关方法)
资源控制示例:
// 限制任务队列防止内存溢出
LinkedBlockingDeque<Runnable> taskQueue = new LinkedBlockingDeque<>(1000);
executor = new ThreadPoolExecutor(4, 4, 0, TimeUnit.SECONDS, taskQueue);
5.内存与性能特性
操作 | 时间复杂度 |
---|---|
插入/移除 | O(1) |
检查元素存在性 | O(n) |
size() |
O(1) |
6.迭代器特性
弱一致性迭代器:
ConcurrentModificationException
安全遍历示例:
List<String> snapshot = new ArrayList<>();
lock.lock(); // 手动加锁获取一致性快照
try {
for (String s : deque) snapshot.add(s);
} finally {
lock.unlock();
}
7.特殊行为约束
null
值(抛出 NullPointerException
)java.util.concurrent
包的内存可见性规则drainTo()
方法提供高效元素转移 List<String> list = new ArrayList<>();
deque.drainTo(list, 100); // 一次性转移100个元素
1.插入操作(以队尾插入为例)
public void putLast(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
Node<E> node = new Node<>(e); // 创建新节点
lock.lock();
try {
while (!linkLast(node)) { // 尝试插入队尾
notFull.await(); // 队列满则阻塞
}
} finally {
lock.unlock();
}
}
private boolean linkLast(Node<E> node) {
if (count.get() >= capacity) return false; // 队列满,插入失败
Node<E> l = last;
node.prev = l;
last = node;
if (first == null) { // 原队列为空
first = node; // 初始化队首
} else {
l.next = node; // 链接到原队尾
}
count.getAndIncrement(); // 元素计数+1
notEmpty.signal(); // 唤醒等待的消费者
return true;
}
2. 移除操作(以队首移除为例)
public E takeFirst() throws InterruptedException {
lock.lock();
try {
E x;
while ((x = unlinkFirst()) == null) { // 尝试移除队首
notEmpty.await(); // 队列空则阻塞
}
return x;
} finally {
lock.unlock();
}
}
private E unlinkFirst() {
Node<E> f = first;
if (f == null) return null; // 队列为空
E item = f.item;
Node<E> next = f.next;
f.item = null; // 断开引用,帮助GC
first = next;
if (next == null) { // 移除后队列变空
last = null; // 重置队尾
} else {
next.prev = null; // 断开原队首链接
}
count.getAndDecrement(); // 元素计数-1
notFull.signal(); // 唤醒等待的生产者
return item;
}
特性 | 实现方式 | 优势 | 局限性 |
---|---|---|---|
线程安全 | 全局锁 (ReentrantLock ) |
实现简单,避免死锁 | 吞吐量低于分段锁 |
容量控制 | count 原子计数器 + capacity 上限 |
精确阻塞/唤醒 | 无界队列可能内存溢出 |
双向操作 | 双向链表 (prev/next 指针) |
支持队首/队尾操作 | 内存开销略高于单向队列 |
阻塞/唤醒 | Condition 条件变量 (notFull/notEmpty ) |
精准通知,避免无效唤醒 | 依赖锁机制 |
空/满判断 | count == 0 或 count == capacity |
时间复杂度 O(1) | - |
LinkedBlockingDeque 通过双向链表 + 全局锁 + 条件等待
实现了线程安全的双端阻塞队列
适用场景:需要高效双端操作的并发场景(如工作窃取、任务调度)
注意事项:
1.无界队列需谨慎使用(可能内存溢出)
2.超高并发场景考虑 ConcurrentLinkedDeque(无锁但非阻塞)
性能关键点
1.锁竞争:
所有操作共用一把锁 → 高并发场景可能成为瓶颈
优化建议:若只需单向队列,优先选 LinkedBlockingQueue
(分离读/写锁)
2.链表维护:
插入/删除需修改相邻节点指针 → 时间复杂度 O(1)
对比数组队列 (ArrayBlockingQueue
):避免扩容但内存不连续
3.内存占用:
每个元素需额外存储两个节点指针 → 空间开销增大
设计权衡与适用场景
场景 | 优势 | 注意事项 |
---|---|---|
工作窃取(Work Stealing) | 天然支持双端操作 | 优于 ConcurrentLinkedDeque 的阻塞特性 |
生产者-消费者模型 | 完备的阻塞机制 | 单锁可能限制吞吐量 |
高优先级任务处理 | putFirst() 支持紧急任务插队 |
需防止任务饥饿 |
有限资源池 | 容量控制防止资源耗尽 | 合理设置队列大小 |
与同类容器对比
特性 | LinkedBlockingDeque |
ArrayBlockingQueue |
ConcurrentLinkedDeque |
---|---|---|---|
数据结构 | 双向链表 | 环形数组 | CAS无锁链表 |
阻塞支持 | ✓ | ✓ | ✗ |
双端操作 | ✓ | ✗ | ✓ |
内存连续性 | ✗ | ✓ | ✗ |
锁机制 | 单锁 | 单锁 | 无锁 |
高并发性能 | 中等 | 中等 | 高 |
public class BasicOperationsDemo {
public static void main(String[] args) {
// 创建容量为3的有界双端队列
LinkedBlockingDeque<String> deque = new LinkedBlockingDeque<>(3);
// 在队尾添加元素
deque.addLast("Task1");
deque.offerLast("Task2"); // 推荐使用非阻塞方法
// 在队首添加元素(高优先级)
deque.addFirst("UrgentTask");
System.out.println("当前队列: " + deque); // [UrgentTask, Task1, Task2]
// 从队首移除元素
String firstTask = deque.removeFirst();
System.out.println("处理任务: " + firstTask); // 处理任务: UrgentTask
// 从队尾移除元素
String lastTask = deque.pollLast();
System.out.println("处理任务: " + lastTask); // 处理任务: Task2
System.out.println("剩余队列: " + deque); // [Task1]
}
}
public class ProducerConsumerDemo {
public static void main(String[] args) throws InterruptedException {
final int CAPACITY = 5;
LinkedBlockingDeque<Integer> taskQueue =
new LinkedBlockingDeque<>(CAPACITY);
AtomicInteger counter = new AtomicInteger(0);
// 生产者线程
Runnable producer = () -> {
try {
while (true) {
int taskId = counter.incrementAndGet();
// 80%概率添加到队尾,20%概率添加到队首(高优先级)
if (Math.random() < 0.8) {
taskQueue.putLast(taskId);
System.out.println("生产普通任务: " + taskId);
} else {
taskQueue.putFirst(taskId);
System.out.println("生产高优先级任务: " + taskId + " ★");
}
TimeUnit.MILLISECONDS.sleep(100); // 模拟生产耗时
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
};
// 消费者线程
Runnable consumer = () -> {
try {
while (true) {
// 从队首获取任务(优先处理高优先级)
Integer task = taskQueue.takeFirst();
System.out.println("消费任务: " + task + " | 剩余: " + taskQueue.size());
TimeUnit.MILLISECONDS.sleep(200); // 模拟处理耗时
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
};
// 启动线程
ExecutorService executor = Executors.newFixedThreadPool(3);
executor.submit(producer);
executor.submit(producer); // 两个生产者
executor.submit(consumer);
// 运行10秒后关闭
TimeUnit.SECONDS.sleep(10);
executor.shutdownNow();
}
}
public class WorkStealingDemo {
public static void main(String[] args) {
final int WORKER_COUNT = 3;
// queues: 一个LinkedBlockingDeque数组,大小为WORKER_COUNT(即3)
LinkedBlockingDeque<Runnable>[] queues = new LinkedBlockingDeque[WORKER_COUNT];
// workers: 一个ExecutorService数组,大小也为WORKER_COUNT
ExecutorService[] workers = new ExecutorService[WORKER_COUNT];
for (int i = 0; i < WORKER_COUNT; i++) {
//初始化每个队列 分布设置成一个容量为10的LinkedBlockingDeque任务队列
queues[i] = new LinkedBlockingDeque<>(10);
final int workerId = i;
//每个线程使用 Executors.newSingleThreadExecutor 创建 单线程执行器
// 每个workerId对应queues[i] 及每个线程中一个容量为10的的队列
workers[i] = Executors.newSingleThreadExecutor(r ->
new Thread(r, "Worker-" + workerId));
}
// 提交初始任务 创建20个简单任务(打印当前线程名)
for (int i = 0; i < 20; i++) {
//任务被均匀分配到3个队列中(使用取模运算 i % WORKER_COUNT)
int queueIdx = i % WORKER_COUNT;
queues[queueIdx].offer(() -> {
System.out.println(Thread.currentThread().getName() + " 执行任务");
});
}
// 工作线程逻辑
for (int i = 0; i < WORKER_COUNT; i++) {
final int workerIndex = i;
workers[i].execute(() -> {
try {
//优先处理自己的任务
while (!Thread.currentThread().isInterrupted()) {
Runnable task = queues[workerIndex].pollFirst
(100, TimeUnit.MILLISECONDS);
if (task != null) {
task.run();
} else {
// 工作窃取:从其他队列尾部偷取任务
for (int j = 0; j < WORKER_COUNT; j++) {
if (j != workerIndex) {
task = queues[j].pollLast();
if (task != null) {
System.out.println(Thread.currentThread().getName() + " 窃取任务");
task.run();
break;// 窃取一个任务后立即跳出
}
}
}
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
}
// 运行5秒后关闭
try {
TimeUnit.SECONDS.sleep(5);
for (ExecutorService worker : workers) {
worker.shutdownNow();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
负载均衡策略
public class TimeoutOperationsDemo {
public static void main(String[] args) {
LinkedBlockingDeque<String> messageQueue = new LinkedBlockingDeque<>(2);
// 生产者线程(快速生产)
new Thread(() -> {
try {
for (int i = 1; i <= 5; i++) {
String msg = "Message-" + i;
// 带超时的插入操作
if (messageQueue.offerLast(msg, 500, TimeUnit.MILLISECONDS)) {
System.out.println("成功发送: " + msg);
} else {
System.out.println("发送超时: " + msg + " (队列已满)");
}
TimeUnit.MILLISECONDS.sleep(300);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
// 消费者线程(慢速消费)
new Thread(() -> {
try {
while (true) {
// 带超时的获取操作
String msg = messageQueue.pollFirst(1, TimeUnit.SECONDS);
if (msg == null) {
System.out.println("等待超时,无新消息");
break;
}
System.out.println("处理消息: " + msg);
TimeUnit.MILLISECONDS.sleep(800);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}).start();
}
}
public class BatchOperationsDemo {
public static void main(String[] args) throws InterruptedException {
LinkedBlockingDeque<Integer> dataQueue = new LinkedBlockingDeque<>(100);
// 生产者填充数据
for (int i = 1; i <= 50; i++) {
dataQueue.put(i);
}
// 批量转移数据(最多转移20个元素)
List<Integer> batch = new ArrayList<>();
int transferred = dataQueue.drainTo(batch, 20);
System.out.println("转移了 " + transferred + " 个元素: " + batch);
// 获取但不移除元素
Integer first = dataQueue.peekFirst();
Integer last = dataQueue.peekLast();
System.out.println("队首: " + first + ", 队尾: " + last);
// 检查元素存在性
boolean contains25 = dataQueue.contains(25);
System.out.println("队列包含25? " + contains25);
// 获取队列状态
System.out.println("当前大小: " + dataQueue.size());
System.out.println("剩余容量: " + dataQueue.remainingCapacity());
// 清空队列
dataQueue.clear();
System.out.println("清空后大小: " + dataQueue.size());
}
}
LinkedBlockingDeque
通过平衡 线程安全、双端灵活性 和 阻塞语义,成为 Java 并发库中实现双端阻塞队列的标准解决方案。其设计在大多数并发场景下表现出良好的稳定性和可预测性。