24、Java并发编程:(JUC集合)LinkedBlockingDeque和ConcurrentLinkedDeque介绍

Queue除了前面介绍的实现外,还有一种双向的Queue实现Deque。这种队列允许在队列头和尾部进行入队出队操作,因此在功能上比Queue显然要更复杂。

LinkedBlockingDeque

我们来看一下该类中的成员变量:

public class LinkedBlockingDeque<E>
        extends AbstractQueue<E>
        implements BlockingDeque<E>,  java.io.Serializable {
   
     

    private static final long serialVersionUID = -387911632671998426L;

    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 transient int count;
    private final int capacity;
    final ReentrantLock lock = new ReentrantLock();
    private final Condition notEmpty = lock.newCondition();
    private final Condition notFull = lock.newCondition();

}

有一个内部类Node,该类用来标记queue的节点,capacity最大为Integer.MAX_VALUE。然后使用了独占锁和条件机制来保证线程安全和进行阻塞控制。从上面的结构上我们可以看出:

1、 要想支持阻塞功能,队列的容量一定是固定的,否则无法在入队的时候挂起线程也就是capacity是final类型的;
2、 既然是双向链表,每一个结点就需要前后两个引用,这样才能将所有元素串联起来,支持双向遍历也即需要prev/next两个引用;
3、 双向链表需要头尾同时操作,所以需要first/last两个节点,当然可以参考LinkedList那样采用一个节点的双向来完成,那样实现起来就稍微麻烦点;
4、 既然要支持阻塞功能,就需要锁和条件变量来挂起线程这里使用一个锁两个条件变量来完成此功能;

上面对LinkedBlockingDeque的结构做了说明,那么原理就很清晰了,无非是用一个独占锁来保持线程安全,然后用Condition来做阻塞操作。双向链表的操作大家都很熟悉就不做过多解释。

ConcurrentLinkedDeque

ConcurrentLinkedDeque是JSR166y中新增的一个无界并发Deque实现,基于已链接节点的、任选范围的双端队列。在迭代时,队列保持弱一致性,但不会抛出ConcurrentModificationException异常。

我们看一下类的成员变量:

public class ConcurrentLinkedDeque<E>
        extends AbstractCollection<E>
        implements Deque<E>, java.io.Serializable {
   
     

    private transient volatile Node<E> head;
    private transient volatile Node<E> tail;

    private static final Node<Object> PREV_TERMINATOR, NEXT_TERMINATOR;

    @SuppressWarnings("unchecked")
    Node<E> prevTerminator() {
        return (Node<E>) PREV_TERMINATOR;
    }

    @SuppressWarnings("unchecked")
    Node<E> nextTerminator() {
        return (Node<E>) NEXT_TERMINATOR;
    }
}

我们看到成员变量里面有头节点和尾节点,然后是节点的引用PREV_TERMINATOR, NEXT_TERMINATOR,
双向链表的结构都是一样的。ConcurrentLinkedDeque不是阻塞队列所以没有用到条件原语。我们在成员变量里面没有看到使用ReentrantLock,因为所有的操作都是使用原子操作,避免了使用独占锁造成性能问题。