zoukankan      html  css  js  c++  java
  • Java多线程之Deque与LinkedBlockingDeque深入分析

    有大小的队列就叫有界队列 如 ArrayBlockingquue, 反之是无界队列 如 LinkedBlockingDeque。 
    单词写错了。
     是的,LinkedBlockingDeque 永远满不了了,但内存用完了,程序就崩了。
     
     

    一、双向队列Deque


    Queue除了前面介绍的实现外,还有一种双向的Queue实现Deque。这种队列允许在队列头和尾部进行入队出队操作,因此在功能上比Queue显然要更复杂。下图描述的是Deque的完整体系图。需要说明的是LinkedList也已经加入了Deque的一部分(LinkedList是从jdk1.2 开始就存在数据结构)。

     


    Deque在Queue的基础上增加了更多的操作方法。


    从上图可以看到,Deque不仅具有FIFO的Queue实现,也有FILO的实现,也就是不仅可以实现队列,也可以实现一个堆栈。

    同时在Deque的体系结构图中可以看到,实现一个Deque可以使用数组(ArrayDeque),同时也可以使用链表(LinkedList),还可以同实现一个支持阻塞的线程安全版本队列LinkedBlockingDeque。


    1、ArrayDeque实现Deque

     


    对于数组实现的Deque来说,数据结构上比较简单,只需要一个存储数据的数组以及头尾两个索引即可。由于数组是固定长度的,所以很容易就得到数组的头和尾,那么对于数组的操作只需要移动头和尾的索引即可。

    特别说明的是ArrayDeque并不是一个固定大小的队列,每次队列满了以后就将队列容量扩大一倍(doubleCapacity()),因此加入一个元素总是能成功,而且也不会抛出一个异常。也就是说ArrayDeque是一个没有容量限制的队列。

    同样继续性能的考虑,使用System.arraycopy复制一个数组比循环设置要高效得多。


    1.1、ArrayDeque的源码解析


     

    1. //数组双端队列ArrayDeque的源码解析  
    2. public class ArrayDeque<E> extends AbstractCollection<E> implements Deque<E>, Cloneable, Serializable{  
    3.     /** 
    4.      * 存放队列元素的数组,数组的长度为“2的指数” 
    5.      */  
    6.     private transient E[] elements;  
    7.     /** 
    8.      *队列的头部索引位置,(被remove()或pop()操作的位置),当为空队列时,首尾index相同 
    9.      */  
    10.     private transient int head;  
    11.     /** 
    12.      * 队列的尾部索引位置,(被 addLast(E), add(E), 或 push(E)操作的位置). 
    13.      */  
    14.     private transient int tail;  
    15.     /** 
    16.      * 队列的最小容量(大小必须为“2的指数”) 
    17.      */  
    18.     private static final int MIN_INITIAL_CAPACITY = 8;  
    19.     // ******  Array allocation and resizing utilities ******  
    20.     /** 
    21.      * 根据所给的数组长度,得到一个比该长度大的最小的2^p的真实长度,并建立真实长度的空数组 
    22.      */  
    23.     private void allocateElements(int numElements) {  
    24.         int initialCapacity = MIN_INITIAL_CAPACITY;  
    25.         if (numElements >= initialCapacity) {  
    26.             initialCapacity = numElements;  
    27.             initialCapacity |= (initialCapacity >>>  1);  
    28.             initialCapacity |= (initialCapacity >>>  2);  
    29.             initialCapacity |= (initialCapacity >>>  4);  
    30.             initialCapacity |= (initialCapacity >>>  8);  
    31.             initialCapacity |= (initialCapacity >>> 16);  
    32.             initialCapacity++;  
    33.             if (initialCapacity < 0)   // Too many elements, must back off  
    34.                 initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements  
    35.         }  
    36.         elements = (E[]) new Object[initialCapacity];  
    37.     }  
    38.     /** 
    39.      * 当队列首尾指向同一个引用时,扩充队列的容量为原来的两倍,并对元素重新定位到新数组中 
    40.      */  
    41.     private void doubleCapacity() {  
    42.         assert head == tail;  
    43.         int p = head;  
    44.         int n = elements.length;  
    45.         int r = n - p; // number of elements to the right of p  
    46.         int newCapacity = n << 1;  
    47.         if (newCapacity < 0)  
    48.             throw new IllegalStateException("Sorry, deque too big");  
    49.         Object[] a = new Object[newCapacity];  
    50.         System.arraycopy(elements, p, a, 0, r);  
    51.         System.arraycopy(elements, 0, a, r, p);  
    52.         elements = (E[])a;  
    53.         head = 0;  
    54.         tail = n;  
    55.     }  
    56.     /** 
    57.      * 拷贝队列中的元素到新数组中 
    58.      */  
    59.     private <T> T[] copyElements(T[] a) {  
    60.         if (head < tail) {  
    61.             System.arraycopy(elements, head, a, 0, size());  
    62.         } else if (head > tail) {  
    63.             int headPortionLen = elements.length - head;  
    64.             System.arraycopy(elements, head, a, 0, headPortionLen);  
    65.             System.arraycopy(elements, 0, a, headPortionLen, tail);  
    66.         }  
    67.         return a;  
    68.     }  
    69.     /** 
    70.      * 默认构造队列,初始化一个长度为16的数组 
    71.      */  
    72.     public ArrayDeque() {  
    73.         elements = (E[]) new Object[16];  
    74.     }  
    75.     /** 
    76.      * 指定元素个数的构造方法 
    77.      */  
    78.     public ArrayDeque(int numElements) {  
    79.         allocateElements(numElements);  
    80.     }  
    81.     /** 
    82.      * 用一个集合作为参数的构造方法 
    83.      */  
    84.     public ArrayDeque(Collection<? extends E> c) {  
    85.         allocateElements(c.size());  
    86.         addAll(c);  
    87.     }  
    88.     //插入和删除的方法主要是: addFirst(),addLast(), pollFirst(), pollLast()。  
    89.     //其他的方法依赖于这些实现。  
    90.     /** 
    91.      * 在双端队列的前端插入元素,元素为null抛异常 
    92.      */  
    93.     public void addFirst(E e) {  
    94.         if (e == null)  
    95.             throw new NullPointerException();  
    96.         elements[head = (head - 1) & (elements.length - 1)] = e;  
    97.         if (head == tail)  
    98.             doubleCapacity();  
    99.     }  
    100.     /** 
    101.      *在双端队列的末端插入元素,元素为null抛异常 
    102.      */  
    103.     public void addLast(E e) {  
    104.         if (e == null)  
    105.             throw new NullPointerException();  
    106.         elements[tail] = e;  
    107.         if ( (tail = (tail + 1) & (elements.length - 1)) == head)  
    108.             doubleCapacity();  
    109.     }  
    110.     /** 
    111.      * 在前端插入,调用addFirst实现,返回boolean类型 
    112.      */  
    113.     public boolean offerFirst(E e) {  
    114.         addFirst(e);  
    115.         return true;  
    116.     }  
    117.     /** 
    118.      * 在末端插入,调用addLast实现,返回boolean类型 
    119.      */  
    120.     public boolean offerLast(E e) {  
    121.         addLast(e);  
    122.         return true;  
    123.     }  
    124.     /** 
    125.      * 删除前端,调用pollFirst实现 
    126.      */  
    127.     public E removeFirst() {  
    128.         E x = pollFirst();  
    129.         if (x == null)  
    130.             throw new NoSuchElementException();  
    131.         return x;  
    132.     }  
    133.     /** 
    134.      * 删除后端,调用pollLast实现 
    135.      */  
    136.     public E removeLast() {  
    137.         E x = pollLast();  
    138.         if (x == null)  
    139.             throw new NoSuchElementException();  
    140.         return x;  
    141.     }  
    142.     //前端出对(删除前端)  
    143.     public E pollFirst() {  
    144.         int h = head;  
    145.         E result = elements[h]; // Element is null if deque empty  
    146.         if (result == null)  
    147.             return null;  
    148.         elements[h] = null;     // Must null out slot  
    149.         head = (h + 1) & (elements.length - 1);  
    150.         return result;  
    151.     }  
    152.     //后端出对(删除后端)  
    153.     public E pollLast() {  
    154.         int t = (tail - 1) & (elements.length - 1);  
    155.         E result = elements[t];  
    156.         if (result == null)  
    157.             return null;  
    158.         elements[t] = null;  
    159.         tail = t;  
    160.         return result;  
    161.     }  
    162.     /** 
    163.      * 得到前端头元素 
    164.      */  
    165.     public E getFirst() {  
    166.         E x = elements[head];  
    167.         if (x == null)  
    168.             throw new NoSuchElementException();  
    169.         return x;  
    170.     }  
    171.     /** 
    172.      * 得到末端尾元素 
    173.      */  
    174.     public E getLast() {  
    175.         E x = elements[(tail - 1) & (elements.length - 1)];  
    176.         if (x == null)  
    177.             throw new NoSuchElementException();  
    178.         return x;  
    179.     }  
    180.     public E peekFirst() {  
    181.         return elements[head]; // elements[head] is null if deque empty  
    182.     }  
    183.     public E peekLast() {  
    184.         return elements[(tail - 1) & (elements.length - 1)];  
    185.     }  
    186.     /** 
    187.      * 移除此双端队列中第一次出现的指定元素(当从头部到尾部遍历双端队列时)。 
    188.      */  
    189.     public boolean removeFirstOccurrence(Object o) {  
    190.         if (o == null)  
    191.             return false;  
    192.         int mask = elements.length - 1;  
    193.         int i = head;  
    194.         E x;  
    195.         while ( (x = elements[i]) != null) {  
    196.             if (o.equals(x)) {  
    197.                 delete(i);  
    198.                 return true;  
    199.             }  
    200.             i = (i + 1) & mask;  
    201.         }  
    202.         return false;  
    203.     }  
    204.     /** 
    205.      * 移除此双端队列中最后一次出现的指定元素(当从头部到尾部遍历双端队列时)。 
    206.      */  
    207.     public boolean removeLastOccurrence(Object o) {  
    208.         if (o == null)  
    209.             return false;  
    210.         int mask = elements.length - 1;  
    211.         int i = (tail - 1) & mask;  
    212.         E x;  
    213.         while ( (x = elements[i]) != null) {  
    214.             if (o.equals(x)) {  
    215.                 delete(i);  
    216.                 return true;  
    217.             }  
    218.             i = (i - 1) & mask;  
    219.         }  
    220.         return false;  
    221.     }  
    222.     // *** 队列方法(Queue methods) ***  
    223.     /** 
    224.      * add方法,添加到队列末端 
    225.      */  
    226.     public boolean add(E e) {  
    227.         addLast(e);  
    228.         return true;  
    229.     }  
    230.     /** 
    231.      * 同上 
    232.      */  
    233.     public boolean offer(E e) {  
    234.         return offerLast(e);  
    235.     }  
    236.     /** 
    237.      * remove元素,删除队列前端 
    238.      */  
    239.     public E remove() {  
    240.         return removeFirst();  
    241.     }  
    242.     /** 
    243.      * 弹出前端(出对,删除前端) 
    244.      */  
    245.     public E poll() {  
    246.         return pollFirst();  
    247.     }  
    248.     public E element() {  
    249.         return getFirst();  
    250.     }  
    251.     public E peek() {  
    252.         return peekFirst();  
    253.     }  
    254.     // *** 栈 方法(Stack methods) ***  
    255.     public void push(E e) {  
    256.         addFirst(e);  
    257.     }  
    258.     public E pop() {  
    259.         return removeFirst();  
    260.     }  
    261.     private void checkInvariants() { ……    }  
    262.     private boolean delete(int i) {   ……   }  
    263.     // *** 集合方法(Collection Methods) ***  
    264.     ……  
    265.     // *** Object methods ***  
    266.     ……  
    267. }  
    268. 整体来说:1个数组,2个index(head 索引和tail索引)。实现比较简单,容易理解。  



    2、LinkedList实现Deque



    对于LinkedList本身而言,数据结构就更简单了,除了一个size用来记录大小外,只有head一个元素Entry。对比Map和Queue的其它数据结构可以看到这里的Entry有两个引用,是双向的队列。

    在示意图中,LinkedList总是有一个“傀儡”节点,用来描述队列“头部”,但是并不表示头部元素,它是一个执行null的空节点。

    队列一开始只有head一个空元素,然后从尾部加入E1(add/addLast),head和E1之间建立双向链接。然后继续从尾部加入E2,E2就在head和E1之间建立双向链接。最后从队列的头部加入E3(push/addFirst),于是E3就在E1和head之间链接双向链接。

    双向链表的数据结构比较简单,操作起来也比较容易,从事从“傀儡”节点开始,“傀儡”节点的下一个元素就是队列的头部,前一个元素是队列的尾部,换句话说,“傀儡”节点在头部和尾部之间建立了一个通道,是整个队列形成一个循环,这样就可以从任意一个节点的任意一个方向能遍历完整的队列。

    同样LinkedList也是一个没有容量限制的队列,因此入队列(不管是从头部还是尾部)总能成功。

     

    3、小结 


    上面描述的ArrayDeque和LinkedList是两种不同方式的实现,通常在遍历和节省内存上ArrayDeque更高效(索引更快,另外不需要Entry对象),但是在队列扩容下LinkedList更灵活,因为不需要复制原始的队列,某些情况下可能更高效。

    同样需要注意的上述两个实现都不是线程安全的,因此只适合在单线程环境下使用,下面章节要介绍的LinkedBlockingDeque就是线程安全的可阻塞的Deque。事实上也应该是功能最强大的Queue实现,当然了实现起来也许会复杂一点。


    二、双向并发阻塞队列 LinkedBlockingDeque


    1、LinkedBlockingDeque数据结构


    双向并发阻塞队列。所谓双向是指可以从队列的头和尾同时操作,并发只是线程安全的实现,阻塞允许在入队出队不满足条件时挂起线程,这里说的队列是指支持FIFO/FILO实现的链表。

     

    首先看下LinkedBlockingDeque的数据结构。通常情况下从数据结构上就能看出这种实现的优缺点,这样就知道如何更好的使用工具了。


    从数据结构和功能需求上可以得到以下结论:

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

    2、LinkedBlockingDeque源码分析


     

    1. public class LinkedBlockingDeque<E> extends AbstractQueue<E> implements BlockingDeque<E>,  java.io.Serializable {  
    2.     /** 包含前驱和后继节点的双向链式结构 */  
    3.     static final class Node<E> {  
    4.  E item;  
    5.         Node<E> prev;  
    6.         Node<E> next;  
    7.         Node(E x, Node<E> p, Node<E> n) {  
    8.             item = x;  
    9.             prev = p;  
    10.             next = n;  
    11.         }  
    12.     }  
    13.     /** 头节点 */  
    14.     private transient Node<E> first;  
    15.     /** 尾节点 */  
    16.     private transient Node<E> last;  
    17.     /** 元素个数*/  
    18.     private transient int count;  
    19.     /** 队列容量 */  
    20.     private final int capacity;  
    21.     /** 锁 */  
    22.     private final ReentrantLock lock = new ReentrantLock();  
    23.     /** notEmpty条件 */  
    24.     private final Condition notEmpty = lock.newCondition();  
    25.     /** notFull条件 */  
    26.     private final Condition notFull = lock.newCondition();  
    27.     /** 构造方法 */  
    28.     public LinkedBlockingDeque() {  
    29.         this(Integer.MAX_VALUE);  
    30.     }  
    31.     public LinkedBlockingDeque(int capacity) {  
    32.         if (capacity <= 0) throw new IllegalArgumentException();  
    33.         this.capacity = capacity;  
    34.     }  
    35.     public LinkedBlockingDeque(Collection<? extends E> c) {  
    36.         this(Integer.MAX_VALUE);  
    37.         for (E e : c)  
    38.             add(e);  
    39.     }  
    40.   
    41.     /** 
    42.      * 添加元素作为新的头节点 
    43.      */  
    44.     private boolean linkFirst(E e) {  
    45.         if (count >= capacity)  
    46.             return false;  
    47.         ++count;  
    48.         Node<E> f = first;  
    49.         Node<E> x = new Node<E>(e, null, f);  
    50.         first = x;  
    51.         if (last == null)  
    52.             last = x;  
    53.         else  
    54.             f.prev = x;  
    55.         notEmpty.signal();  
    56.         return true;  
    57.     }  
    58.     /** 
    59.      * 添加尾元素 
    60.      */  
    61.     private boolean linkLast(E e) {  
    62.         if (count >= capacity)  
    63.             return false;  
    64.         ++count;  
    65.         Node<E> l = last;  
    66.         Node<E> x = new Node<E>(e, l, null);  
    67.         last = x;  
    68.         if (first == null)  
    69.             first = x;  
    70.         else  
    71.             l.next = x;  
    72.         notEmpty.signal();  
    73.         return true;  
    74.     }  
    75.     /** 
    76.      * 返回并移除头节点 
    77.      */  
    78.     private E unlinkFirst() {  
    79.         Node<E> f = first;  
    80.         if (f == null)  
    81.             return null;  
    82.         Node<E> n = f.next;  
    83.         first = n;  
    84.         if (n == null)  
    85.             last = null;  
    86.         else  
    87.             n.prev = null;  
    88.         --count;  
    89.         notFull.signal();  
    90.         return f.item;  
    91.     }  
    92.     /** 
    93.      * 返回并移除尾节点 
    94.      */  
    95.     private E unlinkLast() {  
    96.         Node<E> l = last;  
    97.         if (l == null)  
    98.             return null;  
    99.         Node<E> p = l.prev;  
    100.         last = p;  
    101.         if (p == null)  
    102.             first = null;  
    103.         else  
    104.             p.next = null;  
    105.         --count;  
    106.         notFull.signal();  
    107.         return l.item;  
    108.     }  
    109.     /** 
    110.      * 移除节点x 
    111.      */  
    112.     private void unlink(Node<E> x) {  
    113.         Node<E> p = x.prev;  
    114.         Node<E> n = x.next;  
    115.         if (p == null) {//x是头的情况  
    116.             if (n == null)  
    117.                 first = last = null;  
    118.             else {  
    119.                 n.prev = null;  
    120.                 first = n;  
    121.             }  
    122.         } else if (n == null) {//x是尾的情况  
    123.             p.next = null;  
    124.             last = p;  
    125.         } else {//x是中间的情况  
    126.             p.next = n;  
    127.             n.prev = p;  
    128.         }  
    129.         --count;  
    130.         notFull.signalAll();  
    131.     }  
    132.     //--------------------------------- BlockingDeque 双端阻塞队列方法实现  
    133.     public void addFirst(E e) {  
    134.         if (!offerFirst(e))  
    135.             throw new IllegalStateException("Deque full");  
    136.     }  
    137.     public void addLast(E e) {  
    138.         if (!offerLast(e))  
    139.             throw new IllegalStateException("Deque full");  
    140.     }  
    141.     public boolean offerFirst(E e) {  
    142.         if (e == null) throw new NullPointerException();  
    143.         lock.lock();  
    144.         try {  
    145.             return linkFirst(e);  
    146.         } finally {  
    147.             lock.unlock();  
    148.         }  
    149.     }  
    150.     public boolean offerLast(E e) {  
    151.         if (e == null) throw new NullPointerException();  
    152.         lock.lock();  
    153.         try {  
    154.             return linkLast(e);  
    155.         } finally {  
    156.             lock.unlock();  
    157.         }  
    158.     }  
    159.     public void putFirst(E e) throws InterruptedException {  
    160.         if (e == null) throw new NullPointerException();  
    161.         lock.lock();  
    162.         try {  
    163.             while (!linkFirst(e))  
    164.                 notFull.await();  
    165.         } finally {  
    166.             lock.unlock();  
    167.         }  
    168.     }  
    169.     public void putLast(E e) throws InterruptedException {  
    170.         if (e == null) throw new NullPointerException();  
    171.         lock.lock();  
    172.         try {  
    173.             while (!linkLast(e))  
    174.                 notFull.await();  
    175.         } finally {  
    176.             lock.unlock();  
    177.         }  
    178.     }  
    179.     public boolean offerFirst(E e, long timeout, TimeUnit unit)  
    180.         throws InterruptedException {  
    181.         if (e == null) throw new NullPointerException();  
    182.  long nanos = unit.toNanos(timeout);  
    183.         lock.lockInterruptibly();  
    184.         try {  
    185.             for (;;) {  
    186.                 if (linkFirst(e))  
    187.                     return true;  
    188.                 if (nanos <= 0)  
    189.                     return false;  
    190.                 nanos = notFull.awaitNanos(nanos);  
    191.             }  
    192.         } finally {  
    193.             lock.unlock();  
    194.         }  
    195.     }  
    196.     public boolean offerLast(E e, long timeout, TimeUnit unit)  
    197.         throws InterruptedException {  
    198.         if (e == null) throw new NullPointerException();  
    199.  long nanos = unit.toNanos(timeout);  
    200.         lock.lockInterruptibly();  
    201.         try {  
    202.             for (;;) {  
    203.                 if (linkLast(e))  
    204.                     return true;  
    205.                 if (nanos <= 0)  
    206.                     return false;  
    207.                 nanos = notFull.awaitNanos(nanos);  
    208.             }  
    209.         } finally {  
    210.             lock.unlock();  
    211.         }  
    212.     }  
    213.     public E removeFirst() {  
    214.         E x = pollFirst();  
    215.         if (x == null) throw new NoSuchElementException();  
    216.         return x;  
    217.     }  
    218.     public E removeLast() {  
    219.         E x = pollLast();  
    220.         if (x == null) throw new NoSuchElementException();  
    221.         return x;  
    222.     }  
    223.     public E pollFirst() {  
    224.         lock.lock();  
    225.         try {  
    226.             return unlinkFirst();  
    227.         } finally {  
    228.             lock.unlock();  
    229.         }  
    230.     }  
    231.     public E pollLast() {  
    232.         lock.lock();  
    233.         try {  
    234.             return unlinkLast();  
    235.         } finally {  
    236.             lock.unlock();  
    237.         }  
    238.     }  
    239.     public E takeFirst() throws InterruptedException {  
    240.         lock.lock();  
    241.         try {  
    242.             E x;  
    243.             while ( (x = unlinkFirst()) == null)  
    244.                 notEmpty.await();  
    245.             return x;  
    246.         } finally {  
    247.             lock.unlock();  
    248.         }  
    249.     }  
    250.     public E takeLast() throws InterruptedException {  
    251.         lock.lock();  
    252.         try {  
    253.             E x;  
    254.             while ( (x = unlinkLast()) == null)  
    255.                 notEmpty.await();  
    256.             return x;  
    257.         } finally {  
    258.             lock.unlock();  
    259.         }  
    260.     }  
    261.     public E pollFirst(long timeout, TimeUnit unit)  
    262.         throws InterruptedException {  
    263.  long nanos = unit.toNanos(timeout);  
    264.         lock.lockInterruptibly();  
    265.         try {  
    266.             for (;;) {  
    267.                 E x = unlinkFirst();  
    268.                 if (x != null)  
    269.                     return x;  
    270.                 if (nanos <= 0)  
    271.                     return null;  
    272.                 nanos = notEmpty.awaitNanos(nanos);  
    273.             }  
    274.         } finally {  
    275.             lock.unlock();  
    276.         }  
    277.     }  
    278.     public E pollLast(long timeout, TimeUnit unit)  
    279.         throws InterruptedException {  
    280.  long nanos = unit.toNanos(timeout);  
    281.         lock.lockInterruptibly();  
    282.         try {  
    283.             for (;;) {  
    284.                 E x = unlinkLast();  
    285.                 if (x != null)  
    286.                     return x;  
    287.                 if (nanos <= 0)  
    288.                     return null;  
    289.                 nanos = notEmpty.awaitNanos(nanos);  
    290.             }  
    291.         } finally {  
    292.             lock.unlock();  
    293.         }  
    294.     }  
    295.     public E getFirst() {  
    296.         E x = peekFirst();  
    297.         if (x == null) throw new NoSuchElementException();  
    298.         return x;  
    299.     }  
    300.     public E getLast() {  
    301.         E x = peekLast();  
    302.         if (x == null) throw new NoSuchElementException();  
    303.         return x;  
    304.     }  
    305.     public E peekFirst() {  
    306.         lock.lock();  
    307.         try {  
    308.             return (first == null) ? null : first.item;  
    309.         } finally {  
    310.             lock.unlock();  
    311.         }  
    312.     }  
    313.     public E peekLast() {  
    314.         lock.lock();  
    315.         try {  
    316.             return (last == null) ? null : last.item;  
    317.         } finally {  
    318.             lock.unlock();  
    319.         }  
    320.     }  
    321.     public boolean removeFirstOccurrence(Object o) {  
    322.         if (o == null) return false;  
    323.         lock.lock();  
    324.         try {  
    325.             for (Node<E> p = first; p != null; p = p.next) {  
    326.                 if (o.equals(p.item)) {  
    327.                     unlink(p);  
    328.                     return true;  
    329.                 }  
    330.             }  
    331.             return false;  
    332.         } finally {  
    333.             lock.unlock();  
    334.         }  
    335.     }  
    336.     public boolean removeLastOccurrence(Object o) {  
    337.         if (o == null) return false;  
    338.         lock.lock();  
    339.         try {  
    340.             for (Node<E> p = last; p != null; p = p.prev) {  
    341.                 if (o.equals(p.item)) {  
    342.                     unlink(p);  
    343.                     return true;  
    344.                 }  
    345.             }  
    346.             return false;  
    347.         } finally {  
    348.             lock.unlock();  
    349.         }  
    350.     }  
    351.     //---------------------------------- BlockingQueue阻塞队列 方法实现  
    352.     public boolean add(E e) {  
    353.  addLast(e);  
    354.  return true;  
    355.     }  
    356.     public boolean offer(E e) {  
    357.  return offerLast(e);  
    358.     }  
    359.     public void put(E e) throws InterruptedException {  
    360.  putLast(e);  
    361.     }  
    362.     public boolean offer(E e, long timeout, TimeUnit unit)  
    363.         throws InterruptedException {  
    364.  return offerLast(e, timeout, unit);  
    365.     }  
    366.     public E remove() {  
    367.  return removeFirst();  
    368.     }  
    369.     public E poll() {  
    370.  return pollFirst();  
    371.     }  
    372.     public E take() throws InterruptedException {  
    373.  return takeFirst();  
    374.     }  
    375.     public E poll(long timeout, TimeUnit unit) throws InterruptedException {  
    376.  return pollFirst(timeout, unit);  
    377.     }  
    378.     public E element() {  
    379.  return getFirst();  
    380.     }  
    381.     public E peek() {  
    382.  return peekFirst();  
    383.     }  
    384.     //------------------------------------------- Stack 方法实现  
    385.     public void push(E e) {  
    386.  addFirst(e);  
    387.     }  
    388.     public E pop() {  
    389.  return removeFirst();  
    390.     }  
    391.     //------------------------------------------- Collection 方法实现  
    392.     public boolean remove(Object o) {  
    393.  return removeFirstOccurrence(o);  
    394.     }  
    395.     public int size() {  
    396.         lock.lock();  
    397.         try {  
    398.             return count;  
    399.         } finally {  
    400.             lock.unlock();  
    401.         }  
    402.     }  
    403.     public boolean contains(Object o) {  
    404.         if (o == null) return false;  
    405.         lock.lock();  
    406.         try {  
    407.             for (Node<E> p = first; p != null; p = p.next)  
    408.                 if (o.equals(p.item))  
    409.                     return true;  
    410.             return false;  
    411.         } finally {  
    412.             lock.unlock();  
    413.         }  
    414.     }  
    415.     boolean removeNode(Node<E> e) {  
    416.         lock.lock();  
    417.         try {  
    418.             for (Node<E> p = first; p != null; p = p.next) {  
    419.                 if (p == e) {  
    420.                     unlink(p);  
    421.                     return true;  
    422.                 }  
    423.             }  
    424.             return false;  
    425.         } finally {  
    426.             lock.unlock();  
    427.         }  
    428.     }  
    429.   ……  
    430. }  


    3、LinkedBlockingDeque的优缺点


    有了上面的结论再来研究LinkedBlockingDeque的优缺点。

    优点当然是功能足够强大,同时由于采用一个独占锁,因此实现起来也比较简单。所有对队列的操作都加锁就可以完成。同时独占锁也能够很好的支持双向阻塞的特性。

    凡事有利必有弊。缺点就是由于独占锁,所以不能同时进行两个操作,这样性能上就大打折扣。从性能的角度讲LinkedBlockingDeque要比LinkedBlockingQueue要低很多,比CocurrentLinkedQueue就低更多了,这在高并发情况下就比较明显了。

    前面分析足够多的Queue实现后,LinkedBlockingDeque的原理和实现就不值得一提了,无非是在独占锁下对一个链表的普通操作。


    4、LinkedBlockingDeque的序列化、反序列化


    有趣的是此类支持序列化,但是Node并不支持序列化,因此fist/last就不能序列化,那么如何完成序列化/反序列化过程呢?

    清单4 LinkedBlockingDeque的序列化、反序列化

     

    1. <span style="font-size:14px;">private void writeObject(java.io.ObjectOutputStream s)  
    2.     throws java.io.IOException {  
    3.     lock.lock();  
    4.     try {  
    5.         // Write out capacity and any hidden stuff  
    6.         s.defaultWriteObject();  
    7.         // Write out all elements in the proper order.  
    8.         for (Node<E> p = first; p != null; p = p.next)  
    9.             s.writeObject(p.item);  
    10.         // Use trailing null as sentinel  
    11.         s.writeObject(null);  
    12.     } finally {  
    13.         lock.unlock();  
    14.     }  
    15. }  
    16.   
    17. private void readObject(java.io.ObjectInputStream s)  
    18.     throws java.io.IOException, ClassNotFoundException {  
    19.     s.defaultReadObject();  
    20.     count = 0;  
    21.     first = null;  
    22.     last = null;  
    23.     // Read in all elements and place in queue  
    24.     for (;;) {  
    25.         E item = (E)s.readObject();  
    26.         if (item == null)  
    27.             break;  
    28.         add(item);  
    29.     }  
    30. }  
    31.   
    32.  </span>  


    清单4 描述的是LinkedBlockingDeque序列化/反序列化的过程。序列化时将真正的元素写入输出流,最后还写入了一个null。读取的时候将所有对象列表读出来,如果读取到一个null就表示结束。这就是为什么写入的时候写入一个null的原因,因为没有将count写入流,所以就靠null来表示结束,省一个整数空间。


    参考内容来源:

    集合框架 Queue篇(1)---ArrayDeque
    http://hi.baidu.com/yao1111yao/item/1a1346f65a50d9c8521c266d
    集合框架 Queue篇(7)---LinkedBlockingDeque
    http://hi.baidu.com/yao1111yao/item/b1649cff2cf60be91a111f6d
    深入浅出 Java Concurrency (24): 并发容器 part 9 双向队列集合 Deque
    http://www.blogjava.net/xylz/archive/2010/08/12/328587.html
    深入浅出 Java Concurrency (25): 并发容器 part 10 双向并发阻塞队列 BlockingDeque
    http://www.blogjava.net/xylz/archive/2010/08/18/329227.html

  • 相关阅读:
    前端线上项目汇总
    gulp构建工具学习汇总
    ES6知识点汇总
    页游技术点汇总中
    前后端方案汇总
    vim常用命令
    互联网哲学
    js调试技巧汇总中
    windows下Docker安装MySQL
    RabbitMQ几个常用面试题
  • 原文地址:https://www.cnblogs.com/huangwentian/p/7275023.html
Copyright © 2011-2022 走看看