java常用隊列分析


一、ArrayBlockingQueue

首先看一段源碼:

 1 public class ArrayBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, java.io.Serializable {
 2     private static final long serialVersionUID = -817911632652898426L;
 3     final Object[] items;
 4     int takeIndex;
 5     int putIndex;
 6     int count;
 7     final ReentrantLock lock;
 8     private final Condition notEmpty;
 9     private final Condition notFull;
10     transient Itrs itrs = null;

ArrayBlockingQueue是一個數組隊列,由代碼看其維護了一個Object[] items數組,然后同步保證安全;

理解ArrayBlockingQueue主要理解兩點即可:FIFO原則和同步安全訪問。

 

①、既然是使用數組實現的隊列,那么他如何保證隊列的FIFO原則的呢?主要有三個序列控制:

  takeIndex(items index for next take, poll, peek or remove),即從隊列中獲取元素時的控制序列;

  putIndex(items index for next put, offer, or add),即往隊列中增加元素的控制序列;

  count(Number of elements in the queue),即隊列中的元素數量序列;

  takeIndex每移除一個元素takeIndex則+1(peek方法比較特殊,不會移除元素),當tokeIndex+1的值等於數組items長度時,takeIndex置0。

  putIndex每增加一個元素+1,當putIndex+1的值等於數組items的長度時,putIndex置0.

  count每增加一個元素+1,count每移除一個元素-1.

  由此可見,數組基於這三個變量的循環控制,實現了隊列的FIFO。= =、

②、並發安全是基於ReentrantLock和兩個Condition實現的。

  看一下構造方法:

1 public ArrayBlockingQueue(int capacity, boolean fair) {
2     if (capacity <= 0)
3         throw new IllegalArgumentException();
4     this.items = new Object[capacity];
5     lock = new ReentrantLock(fair);
6     notEmpty = lock.newCondition();
7     notFull =  lock.newCondition();
8 }

 

  使用單參構造時這個fair參數默認是false,即非公平鎖。基於ReentrantLock和兩個Condition的阻塞和喚醒實現同步;notEmpty在隊列中沒有元素可獲取時阻塞線程,notFull在滿隊列不可插入時阻塞線程。

二、LinkedBlockingQueue

還是先看段源碼:

 1 public class LinkedBlockingQueue<E> extends AbstractQueue<E>
 2         implements BlockingQueue<E>, java.io.Serializable {
 3     private static final long serialVersionUID = -6903933977591709194L;
 4 
 5     static class Node<E> {
 6         E item;
 7         Node<E> next;
 8 
 9         Node(E x) { item = x; }
10     }
11 
12     private final int capacity;
13 
14     private final AtomicInteger count = new AtomicInteger();
15 
16     transient Node<E> head;
17 
18     private transient Node<E> last;
19 
20     private final ReentrantLock takeLock = new ReentrantLock();
21 
22     private final Condition notEmpty = takeLock.newCondition();
23 
24     private final ReentrantLock putLock = new ReentrantLock();
25 
26     private final Condition notFull = putLock.newCondition();

LinkedBlockingQueue和ArrayBlockingQueue有很多相似的地方,主要區別如下:

  ①LinkedBlockingQueue使用的是單向鏈表,而ArrayBlockingQueue使用的是數組,故LinkedBlockingQueue使用head節點和last節點維護FIFO原則。

  ②LinkedBlockingQueue分別使用了takeLock和putLock兩個鎖進行新增和移除元素的操作,這也導致了元素計數器count屬性需要聲明為AtomicInteger進行原子操作。

  ③LinkedBlockingQueue默認可以不指定隊列大小,使用Integer.MAX_VALUE默認初始化隊列大小。

 

三、PriorityBlockingQueue帶有優先級的隊列

看源碼中屬性部分:

public class PriorityBlockingQueue<E> extends AbstractQueue<E>
    implements BlockingQueue<E>, java.io.Serializable {
    private static final long serialVersionUID = 5595510919245408276L;

    private static final int DEFAULT_INITIAL_CAPACITY = 11;

    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

    private transient Object[] queue;

    private transient int size;

    private transient Comparator<? super E> comparator;

    private final ReentrantLock lock;

    private final Condition notEmpty;

    private transient volatile int allocationSpinLock;

    private PriorityQueue<E> q;

其仍然是個數組,和ArrayBlockingQueue很像,也是通過單個lock加鎖,其特點如下:

  ①、初始大小11,但是會自動擴容,最大可以到Integer.MAX_VALUE - 8;

  ②、其隊列元素必須實現Comparator接口,以便其基於完全二叉樹的最小堆和最大堆排序;

  ③、PriorityBlockingQueue本身不支持序列化,數組前加了transient修飾,其序列化會轉化成PriorityQueue,反序列化時再轉換成PriorityBlockingQueue自己。

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM