LinkedBlockingDeque介绍
LinkedBlockingDeque是双向链表实现的阻塞队列。该阻塞队列同时支持FIFO和FILO两种操作方式,即可以从队列的头和尾同时操作(插入/删除);
此外,LinkedBlockingDeque还是可选容量的,防止过度膨胀,默认等于Integer.MAX_VALUE。
LinkedBlockingDeque的uml图
说明:
- LinkedBlockingDeque继承于AbstractQueue,它本质上是一个支持FIFO和FILO的双向的队列。
- LinkedBlockingDeque实现了BlockingDeque接口,它支持多线程并发。当多线程竞争同一个资源时,某线程获取到该资源之后,其它线程需要阻塞等待。
- LinkedBlockingDeque是通过双向链表实现的。
3.1 first是双向链表的表头。
3.2 last是双向链表的表尾。
3.3 count是LinkedBlockingDeque的实际大小,即双向链表中当前节点个数。
3.4 capacity是LinkedBlockingDeque的容量,它是在创建LinkedBlockingDeque时指定的。
3.5 lock是控制对LinkedBlockingDeque的互斥锁,当多个线程竞争同时访问LinkedBlockingDeque时,某线程获取到了互斥锁lock,其它线程则需要阻塞等待,直到该线程释放lock,其它线程才有机会获取lock从而获取cpu执行权。
3.6 notEmpty和notFull分别是“非空条件”和“未满条件”。通过它们能够更加细腻进行并发控制。
LinkedBlockingDeque源码分析
构造方法
public LinkedBlockingDeque(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
}
其他成员的初始化:
// “双向队列”的表头
transient Node<E> first;
// “双向队列”的表尾
transient Node<E> last;
// 节点数量
private transient int count;
// 容量
private final int capacity;
// 互斥锁 , 互斥锁对应的“非空条件notEmpty”, 互斥锁对应的“未满条件notFull”
final ReentrantLock lock = new ReentrantLock();
private final Condition notEmpty = lock.newCondition();
private final Condition notFull = lock.newCondition();
添加方法
public void put(E e) throws InterruptedException {
putLast(e);
}
putLast方法
public void putLast(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 (!linkLast(node))
notFull.await();
} finally {
lock.unlock();
}
}
linkLast方法
private boolean linkLast(Node<E> node) {
// 如果“双向链表的节点数量” > “容量”,则返回false,表示插入失败。
if (count >= capacity)
return false;
// 将“node添加到链表末尾”,并设置node为新的尾节点
Node<E> l = last;
node.prev = l;
last = node;
if (first == null)
first = node;
else
l.next = node;
// 将“节点数量”+1
++count;
// 插入节点之后,唤醒notEmpty上的等待线程。
notEmpty.signal();
return true;
}
如果要删除元素,则调用take方法,take方法跟put方法镜像,方法比较类似,就不展示出来啦。
LinkedBlockingDueue与LinkedBlockingQueue的区别:
LinkedBlockingDueue没有进行读写锁的分离,因此同一时间只能有一个线程对其操作,因此在高并发应用中,它的性能要远远低于LinkedBlockingQueue。