浪子归家|Java并发编程-LinkedBlockingDeque详解

简介LinkedBlockingDeque是一个由链表结构组成的双向阻塞队列 , 即可以从队列的两端插入和移除元素 。 双向队列因为多了一个操作队列的入口 , 在多线程同时入队时 , 也就减少了一半的竞争 。
相比于其他阻塞队列 , LinkedBlockingDeque多了addFirst、addLast、peekFirst、peekLast等方法 , 以first结尾的方法 , 表示插入、获取或移除双端队列的第一个元素 。 以last结尾的方法 , 表示插入、获取或移除双端队列的最后一个元素 。
LinkedBlockingDeque是可选容量的 , 在初始化时可以设置容量防止其过度膨胀 , 如果不设置 , 默认容量大小为Integer.MAX_VALUE 。
LinkedBlockingDeque类有三个构造方法:
public LinkedBlockingDeque()public LinkedBlockingDeque(int capacity)public LinkedBlockingDeque(Collection c)LinkedBlockingDeque源码详解LinkedBlockingDeque类定义为:
public class LinkedBlockingDeque extends AbstractQueue implements BlockingDeque, java.io.Serializable该类继承自AbstractQueue抽象类 , 又实现了BlockingDeque接口 , 下面介绍一个BlockingDeque接口 , 该接口定义如下:
public interface BlockingDeque extends BlockingQueue, DequeBlockingDeque继承自BlockingQueue和Deque接口 , BlockingDeque接口定义了在双端队列中常用的方法 。
LinkedBlockingDeque类中的数据都被封装成了Node对象:
static final class Node {E item;Node prev;Node next;Node(E x) {item = x;}}LinkedBlockingDeque类中的重要字段如下:
// 队列双向链表首节点transient Node first;// 队列双向链表尾节点transient Node last;// 双向链表元素个数private transient int count;// 双向链表最大容量private final int capacity;// 全局独占锁final ReentrantLock lock = new ReentrantLock();// 非空Condition对象private final Condition notEmpty = lock.newCondition();// 非满Condition对象private final Condition notFull = lock.newCondition();LinkedBlockingDeque类的底层实现和LinkedBlockingQueue类很相似 , 都有一个全局独占锁 , 和两个Condition对象 , 用来阻塞和唤醒线程 。
LinkedBlockingDeque类对元素的操作方法比较多 , 我们下面以putFirst、putLast、pollFirst、pollLast方法来对元素的入队、出队操作进行分析 。
入队putFirst(E e)方法是将指定的元素插入双端队列的开头 , 源码如下:
public void putFirst(E e) throws InterruptedException {// 若插入元素为null , 则直接抛出NullPointerException异常if (e == null) throw new NullPointerException();// 将插入节点包装为Node节点Node node = new Node(e);// 获取全局独占锁final ReentrantLock lock = this.lock;lock.lock();try {while (!linkFirst(node))notFull.await();} finally {// 释放全局独占锁lock.unlock();}}入队操作是通过linkFirst(E e)方法来完成的 , 如下所示:
private boolean linkFirst(Node node) {// assert lock.isHeldByCurrentThread();// 元素个数超出容量 。 直接返回falseif (count >= capacity)return false;// 获取双向链表的首节点Node f = first;// 将node设置为首节点node.next = f;first = node;// 若last为null , 设置尾节点为node节点if (last == null)last = node;else// 更新原首节点的前驱节点f.prev = node;++count;// 唤醒阻塞在notEmpty上的线程notEmpty.signal();return true;}若入队成功 , 则linkFirst(E e)方法返回true , 否则 , 返回false 。 若该方法返回false , 则当前线程会阻塞在notFull条件上 。


推荐阅读