一、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自己。
