【源码】JUC —— BlockingDeque LinkedBlockingDeque 浅析
前言
public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E>
BlockingDeque 拓展了 BlockingQueue 和 Deque,即它是可以是一个 队列、一个 双端队列,同时也支持插入和取出时的 可阻塞 操作
关于 Queue BlockingQueue 相关,可以阅读下面文章
【源码】Queue AbstractQueue PriorityQueue 浅析
【源码】JUC —— BlockingQueue ArrayBlockingQueue 浅析
BlockingDeque
BlockingDeque 的方法整理如下
头部操作 | 失败时抛异常 | 返回特定值 | 阻塞操作 | 定时阻塞操作 |
---|---|---|---|---|
插入 | addFirst(e) | offerFirst(e) | putFirst(e) | offerFirst(e, time, unit) |
取出 | removeFirst() | pollFirst() | takeFirst() | pollFirst(time, unit) |
检索 | getFirst() | peekFirst() | —— | —— |
尾部操作 | 失败时抛异常 | 返回特定值 | 阻塞操作 | 定时阻塞操作 |
---|---|---|---|---|
插入 | addLast(e) | offerLast(e) | putLast(e) | offerLast(e, time, unit) |
取出 | removeLast() | pollLast() | takeLast() | pollLast(time, unit) |
检索 | getLast() | peekLast() | —— | —— |
同时,BlockingDeque 继承了 BlockingQueue,因为也可以当做一个 FIFO 的单向队列使用,对应的方法映射如下
对比 | BlockingQueue | BlockingDeque |
---|---|---|
插入 | add(e) | addLast(e) |
offer(e) | offerLast(e) | |
put(e) | putLast(e) | |
offer(e, time, unit) | offerLast(e, time, unit) | |
取出 | remove() | removeFirst() |
poll() | pollFirst() | |
take() | takeFirst() | |
poll(time, unit) | pollFirst(time, unit) | |
检索 | element() | getFirst() |
peek() | peekFirst() |
LinkedBlockingDeque
基于 链表 实现的 BlockingDeque
Node
static final class Node<E> {
// 元素
E item;
// 前置
Node<E> prev;
// 后置
Node<E> next;
Node(E x) {
item = x;
}
}
链表的节点以内部类 Node 维护,属性包括 元素、前置节点 和 后置节点
属性、构造方法
// 头节点
transient Node<E> first;
// 尾节点
transient Node<E> last;
// 元素个数
private transient int count;
// 容量
private final int capacity;
// 锁对象
final ReentrantLock lock = new ReentrantLock();
// take 操作的阻塞队列
private final Condition notEmpty = lock.newCondition();
// put 操作的阻塞队列
private final Condition notFull = lock.newCondition();
----------------- 构造方法 ----------------------
// 默认容量 Integer.MAX_VALUE
public LinkedBlockingDeque() {
this(Integer.MAX_VALUE);
}
// 自定义容量
public LinkedBlockingDeque(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
}
// 以指定集合初始化
public LinkedBlockingDeque(Collection<? extends E> c) {
this(Integer.MAX_VALUE);
addAll(c);
}
不同于 ArrayBlockingQueue 的是,可以不指定 容量,默认为 Integer.MAX_VALUE
头插入操作
public void addFirst(E e) {
// 插入失败抛出异常
if (!offerFirst(e))
throw new IllegalStateException("Deque full");
}
public boolean offerFirst(E e) {
if (e == null) throw new NullPointerException();
Node<E> node = new Node<E>(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
return linkFirst(node);
} finally {
lock.unlock();
}
}
public void putFirst(E e) throws InterruptedException {
if (e == null) throw new NullPointerException();
Node<E> node = new Node<E>(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
// 如果插入失败(队列满了)则阻塞直到插入成功
while (!linkFirst(node))
notFull.await();
} finally {
lock.unlock();
}
}
public boolean offerFirst(E e, long timeout, TimeUnit unit)
throws InterruptedException {
if (e == null) throw new NullPointerException();
Node<E> node = new Node<E>(e);
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
// 如果插入失败(队列满了)则阻塞指定时间(nanos)
while (!linkFirst(node)) {
if (nanos <= 0L)
return false;
nanos = notFull.awaitNanos(nanos);
}
return true;
} finally {
lock.unlock();
}
}
private boolean linkFirst(Node<E> node) {
/**
* 必须持有锁
* 容量已满插入失败,返回 false
*
* f 指向 first
* node 的 next 指向 first
* first 指向 node
* 如果 last == null,即首次插入
* 则 last 也指向 node
* 否则 f 的 prev 指向 node
*
* 元素个数累加
* notEmpty 唤醒一个 take 线程
*/
if (count >= capacity)
return false;
Node<E> f = first;
node.next = f;
first = node;
if (last == null)
last = node;
else
f.prev = node;
++count;
notEmpty.signal();
return true;
}
最终由 linkFirst
实现节点插入,具体的细节可见注释
尾插入操作
跟 头插入操作 同理,略
头获取操作
public E removeFirst() {
E x = pollFirst();
// 获取失败(队列为空),则抛出异常
if (x == null) throw new NoSuchElementException();
return x;
}
public E pollFirst() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return unlinkFirst();
} finally {
lock.unlock();
}
}
public E takeFirst() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lock();
try {
E x;
// 若队列为空,则阻塞直到取到元素
while ( (x = unlinkFirst()) == null)
notEmpty.await();
return x;
} finally {
lock.unlock();
}
}
public E pollFirst(long timeout, TimeUnit unit)
throws InterruptedException {
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
E x;
// 若队列为空,则阻塞指定时间(nanos)
while ( (x = unlinkFirst()) == null) {
if (nanos <= 0L)
return null;
nanos = notEmpty.awaitNanos(nanos);
}
return x;
} finally {
lock.unlock();
}
}
private E unlinkFirst() {
/**
* 必须持有锁
*
* f 指向 node
* f == null,即队列为空,返回 null
* n 指向 f 的 next
* item 指向 f 的 item
* f 的 item 置为 null
* f 的 next 指向 f,此时 f 可以被 GC 回收了
*
* first 指向 n
* 如果 n == null,则队列为空了,last 也指向 null
* 否则 n 的 prev 指向 null
* 元素个数递减
* 唤醒一个 put 操作线程
*/
Node<E> f = first;
if (f == null)
return null;
Node<E> n = f.next;
E item = f.item;
f.item = null;
f.next = f; // help GC
first = n;
if (n == null)
last = null;
else
n.prev = null;
--count;
notFull.signal();
return item;
}
最终由 unlinkFirst
实现节点获取,具体的细节可见注释
尾获取操作
跟 头获取操作 同理,略
检索操作
public E getFirst() {
E x = peekFirst();
// 没有元素则抛出异常
if (x == null) throw new NoSuchElementException();
return x;
}
public E peekFirst() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
// 返回 first 的 item
return (first == null) ? null : first.item;
} finally {
lock.unlock();
}
}
头检索 即返回 first.item
,同理 尾检索 返回 last.item
BlockingQueue 方法委托
上文提到 BlockingDeque 也是一个 BlockingQueue,也给出了方法的对应关系,那么实现就是简单的方法委托
public boolean add(E e) {
addLast(e);
return true;
}
public boolean offer(E e) {
return offerLast(e);
}
public void put(E e) throws InterruptedException {
putLast(e);
}
public boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException {
return offerLast(e, timeout, unit);
}
public E remove() {
return removeFirst();
}
public E poll() {
return pollFirst();
}
public E take() throws InterruptedException {
return takeFirst();
}
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
return pollFirst(timeout, unit);
}
public E element() {
return getFirst();
}
public E peek() {
return peekFirst();
}
Stack 方法委托
同理,BlockingDeque 也可以是个 Stack(栈)
public void push(E e) {
addFirst(e);
}
public E pop() {
return removeFirst();
}
其他方法
LinkedBlockingDeque 还实现了诸如 remove(Object o)
size()
contains(Object o)
addAll(Collection<? extends E> c)
等很多方法,不再一一解读了
总结
本文介绍了 BlockingDeque 接口和基于链表实现的 LinkedBlockingDeque ,同时支持 队列、双端队列 的 可阻塞 操作,十分强大