Queue常用類解析之PriorityQueue
Queue常用類解析之ConcurrentLinkedQueue
Queue常用類解析之BlockingQueue(一):PriorityBlockingQueue、DelayQueue和DelayedWorkQueue
接着上文對BlockingQueue的介紹繼續向下
五、ArrayBlockingQueue
從命名可以看出,這是一個循環數組表示的的阻塞隊列。
與前面介紹的BlockingQueue不同,ArrayBlockingQueue在入隊和出隊時都有可能會陷入阻塞。
1. 屬性
/** The queued items */
final Object[] items;
/** items index for next take, poll, peek or remove */
int takeIndex;
/** items index for next put, offer, or add */
int putIndex;
/** Number of elements in the queue */
int count;
/*
* Concurrency control uses the classic two-condition algorithm
* found in any textbook.
*/
/** Main lock guarding all access */
final ReentrantLock lock;
/** Condition for waiting takes */
private final Condition notEmpty;
/** Condition for waiting puts */
private final Condition notFull;
/**
* Shared state for currently active iterators, or null if there
* are known not to be any. Allows queue operations to update
* iterator state.
*/
transient Itrs itrs = null;
putIndex和takeIndex分別表示入隊和出隊的數組索引。
notEmpty和notFull分別表示空隊列和滿隊列時的阻塞condition。
Itrs 時迭代器的鏈表形式的集合。
2. 構造器
public ArrayBlockingQueue(int capacity) {
this(capacity, false);
}
public ArrayBlockingQueue(int capacity, boolean fair) {
if (capacity <= 0)
throw new IllegalArgumentException();
this.items = new Object[capacity];
lock = new ReentrantLock(fair);
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
ArrayBlockingQueue由兩個構造器方法,不支持無參構造器。至少需要傳入隊列的容量,並初始化對於長度的數組。後續數組的長度無法修改。另外,ArrayBlockingQueue還支持在構造器方法中傳入是否是公平鎖的參數,默認是非公平鎖。
3. ArrayBlockingQueue#put(Object)
public void put(E e) throws InterruptedException {
//元素不能爲null
checkNotNull(e);
//加鎖
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
//滿隊列,線程阻塞
while (count == items.length)
notFull.await();
//入隊操作
enqueue(e);
} finally {
lock.unlock();
}
}
private void enqueue(E x) {
// assert lock.getHoldCount() == 1;
// assert items[putIndex] == null;
final Object[] items = this.items;
//putIndex入隊的數組索引
items[putIndex] = x;
//循環數組,到達數組末尾後的下一個索引爲0
if (++putIndex == items.length)
putIndex = 0;
count++;
//發送notEmpty信號喚醒
notEmpty.signal();
}
4. ArrayBlockingQueue#poll(long, TimeUnit)
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
//計算阻塞時間
long nanos = unit.toNanos(timeout);
//加鎖
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
//空隊列,線程阻塞
while (count == 0) {
if (nanos <= 0)
return null;
nanos = notEmpty.awaitNanos(nanos);
}
//執行出隊操作
return dequeue();
} finally {
lock.unlock();
}
}
private E dequeue() {
// assert lock.getHoldCount() == 1;
// assert items[takeIndex] != null;
final Object[] items = this.items;
@SuppressWarnings("unchecked")
//takeIndex出隊的數組索引
E x = (E) items[takeIndex];
items[takeIndex] = null;
//循環數組,達到數組末尾的下一個元素是0
if (++takeIndex == items.length)
takeIndex = 0;
count--;
//itrs操作
if (itrs != null)
itrs.elementDequeued();
//發送notFull信號喚醒線程
notFull.signal();
return x;
}
5. ArrayBlockingQueue#remove(Object)
public boolean remove(Object o) {
if (o == null) return false;
final Object[] items = this.items;
//加鎖
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count > 0) {
final int putIndex = this.putIndex;
int i = takeIndex;
//從takeIndex開始遍歷,直到putIndex - 1
do {
//相等,執行刪除邏輯
if (o.equals(items[i])) {
removeAt(i);
return true;
}
if (++i == items.length)
i = 0;
} while (i != putIndex);
}
return false;
} finally {
lock.unlock();
}
}
void removeAt(final int removeIndex) {
// assert lock.getHoldCount() == 1;
// assert items[removeIndex] != null;
// assert removeIndex >= 0 && removeIndex < items.length;
final Object[] items = this.items;
//相當於poll
if (removeIndex == takeIndex) {
// removing front item; just advance
items[takeIndex] = null;
if (++takeIndex == items.length)
takeIndex = 0;
count--;
if (itrs != null)
itrs.elementDequeued();
} else {
// an "interior" remove
// slide over all others up through putIndex.
final int putIndex = this.putIndex;
//從removeIndex到putIndex - 1的元素依次向前挪動一位,並putIndex = putIndex - 1
for (int i = removeIndex;;) {
int next = i + 1;
if (next == items.length)
next = 0;
if (next != putIndex) {
items[i] = items[next];
i = next;
} else {
items[i] = null;
this.putIndex = i;
break;
}
}
count--;
//itrs處理
if (itrs != null)
itrs.removedAt(removeIndex);
}
//發送信號
notFull.signal();
}
6. Itrs
Itr是ArrayBlockingQueue的迭代器類,而Itrs則是由Itr組成的鏈表集合類。
對於Itrs和Itr,因爲循環數組和移除元素時會使迭代器丟失他們的位置,爲了保證迭代器和隊列數據的一致性,需要注意
(1)記錄所有takeIndex循環到0的次數
(2)每次刪除元素都需要通過removeAt方法通知到所有的迭代器。
Itrs中使用的是Itr的弱引用類,因此需要針對Itrs中的過期迭代器進行清理。清理過期節點主要有3個時機:
(1)建立了新的迭代器,調用doSomeSweep
(2)takeIndex循環到0,調用takeIndexWrapped
(3)隊列變爲空隊列時,調用queueIsEmpty
6.1 屬性
/** Incremented whenever takeIndex wraps around to 0 */
//takeIndex 循環到0的次數
int cycles = 0;
/** Linked list of weak iterator references */
//頭結點
private Node head;
/** Used to expunge stale iterators */
//清理過期迭代器的開始節點
private Node sweeper = null;
//清理過期迭代器時的節點檢查數量,harder模式時16,否則4
private static final int SHORT_SWEEP_PROBES = 4;
private static final int LONG_SWEEP_PROBES = 16;
6.2 Itrs#register(Itr)
void register(Itr itr) {
// assert lock.getHoldCount() == 1;
head = new Node(itr, head);
}
迭代器加入Itrs鏈表,頭插法。
6.3 Itrs#doSomeSweep(boolean)
void doSomeSweeping(boolean tryHarder) {
// assert lock.getHoldCount() == 1;
// assert head != null;
int probes = tryHarder ? LONG_SWEEP_PROBES : SHORT_SWEEP_PROBES;
Node o, p;
final Node sweeper = this.sweeper;
//passGo表示已經從頭結點開始檢查
boolean passedGo; // to limit search to one full sweep
if (sweeper == null) {
o = null;
p = head;
passedGo = true;
} else {
o = sweeper;
p = o.next;
passedGo = false;
}
for (; probes > 0; probes--) {
if (p == null) {
//已經檢查到尾節點,並且檢查是從頭節點開始的,跳出循環
if (passedGo)
break;
//已經檢查到尾節點,但沒有檢查過頭結點,從頭節點開始再次檢查,並記錄passedGo=true
o = null;
p = head;
passedGo = true;
}
final Itr it = p.get();
final Node next = p.next;
//過期的無效節點
if (it == null || it.isDetached()) {
// found a discarded/exhausted iterator
//一旦找到一個過期的節點,就會採用harder模式檢查更多的節點。
probes = LONG_SWEEP_PROBES; // "try harder"
// unlink p
p.clear();
p.next = null;
if (o == null) {
head = next;
//鏈表中已經沒有迭代器
if (next == null) {
// We've run out of iterators to track; retire
itrs = null;
return;
}
}
else
o.next = next;
} else {
o = p;
}
p = next;
}
//記錄sweeper節點,下一次清理直接從sweeper 開始
this.sweeper = (p == null) ? null : o;
}
該方法用於檢查並清理過期節點,參數爲是否採用harder模式,harder模式檢查16個節點,非harder模式檢查4個節點。
一旦找到一個過期的節點,就會採用harder模式檢查更多的節點。
檢查並清理的動作在循環中進行,檢查結束的條件由以下3個,滿足其一就可以離開循環。
(1)檢查的節點數量達到要求
(2)鏈表已經完整了檢查了一遍。
(3)鏈表中已經沒有迭代器
6.4 Itrs#takeIndexWrapped()
void takeIndexWrapped() {
// assert lock.getHoldCount() == 1;
cycles++;
for (Node o = null, p = head; p != null;) {
final Itr it = p.get();
final Node next = p.next;
if (it == null || it.takeIndexWrapped()) {
// unlink p
// assert it == null || it.isDetached();
p.clear();
p.next = null;
if (o == null)
head = next;
else
o.next = next;
} else {
o = p;
}
p = next;
}
if (head == null) // no more iterators to track
itrs = null;
}
takeIndex每次循環到0時會調用該方法。
cycle計數增加,遍歷鏈表檢查並清理過期的無效節點。
6.5 Itrs#queueIsEmpty()
void queueIsEmpty() {
// assert lock.getHoldCount() == 1;
for (Node p = head; p != null; p = p.next) {
Itr it = p.get();
if (it != null) {
p.clear();
it.shutdown();
}
}
head = null;
itrs = null;
}
隊列每次變爲空隊列時調用,清空所有有效的迭代器。
6.6 Itrs#elementDequeued()
void elementDequeued() {
// assert lock.getHoldCount() == 1;
if (count == 0)
queueIsEmpty();
else if (takeIndex == 0)
takeIndexWrapped();
}
在ArrayBlockingQueue中移除元素(包括出隊和remove)時調用,保證迭代器和隊列數據的一致性。
7. Itr
Itr是ArrayBlockingQueue的迭代器,所有迭代器都會記錄到對應ArrayBlockingQueue的itrs屬性中。
Itr是一個先讀後寫的迭代器,會先預讀到next的值進行保存,即使後續next對應的元素被移除也能通過迭代器的next方法訪問到。這是爲了防止hasNext方法執行和next方法執行的之間的某個時刻,該節點被刪除,導致hasNext返回true而next返回值卻爲null的情形,因此ArrayBlockingQueue的迭代器Itr是弱一致性的。
當迭代器的所有下標都爲負數或者hasNext第一次返回false時進入detach狀態,表示迭代器已經無用,可以從itrs中移除。
7.1 屬性
/** Index to look for new nextItem; NONE at end */
//遊標,下一個next節點對應的下標,到達putIndex結束的位置爲NONE
private int cursor;
/** Element to be returned by next call to next(); null if none */
//next節點的元素值
private E nextItem;
/** Index of nextItem; NONE if none, REMOVED if removed elsewhere */
//next節點的下標
private int nextIndex;
/** Last element returned; null if none or not detached. */
//上一個節點的元素值
private E lastItem;
/** Index of lastItem, NONE if none, REMOVED if removed elsewhere */
//上一個節點的下標
private int lastRet;
/** Previous value of takeIndex, or DETACHED when detached */
//記錄takeIndex
private int prevTakeIndex;
/** Previous value of iters.cycles */
//記錄cycles
private int prevCycles;
/** Special index value indicating "not available" or "undefined" */
private static final int NONE = -1;
/**
* Special index value indicating "removed elsewhere", that is,
* removed by some operation other than a call to this.remove().
*/
private static final int REMOVED = -2;
/** Special value for prevTakeIndex indicating "detached mode" */
private static final int DETACHED = -3;
可以看到,每次都會預讀到next的值存儲到nextItem和nextIndex,並且保存上一次的返回值lastItem和lastRet。
由於ArrayBlockingQueue時一個循環數組,takeIndex和putIndex一直在循環移動。這樣子就有可能出現,迭代器創建時位置已經確定,但是隨着ArrayBlockingQueue數組不斷循環,位置一直在變化,導致迭代器的結果有誤。因此需要記錄舊的takeIndex和cycles,在遍歷時比較這兩個值與當前值,以修正下標索引,保證遍歷的正確性。
7.2 Itr#Itr()
Itr() {
// assert lock.getHoldCount() == 0;
lastRet = NONE;
final ReentrantLock lock = ArrayBlockingQueue.this.lock;
lock.lock();
try {
//沒有元素,無用的迭代器,直接進入detach模式
if (count == 0) {
// assert itrs == null;
cursor = NONE;
nextIndex = NONE;
prevTakeIndex = DETACHED;
} else {
//保存各項屬性
final int takeIndex = ArrayBlockingQueue.this.takeIndex;
prevTakeIndex = takeIndex;
nextItem = itemAt(nextIndex = takeIndex);
cursor = incCursor(takeIndex);
//迭代器加入itrs
if (itrs == null) {
itrs = new Itrs(this);
} else {
itrs.register(this); // in this order
itrs.doSomeSweeping(false);
}
prevCycles = itrs.cycles;
// assert takeIndex >= 0;
// assert prevTakeIndex == takeIndex;
// assert nextIndex >= 0;
// assert nextItem != null;
}
} finally {
lock.unlock();
}
}
7.3 Itr#incorporateDequeues()
private void incorporateDequeues() {
// assert lock.getHoldCount() == 1;
// assert itrs != null;
// assert !isDetached();
// assert count > 0;
final int cycles = itrs.cycles;
final int takeIndex = ArrayBlockingQueue.this.takeIndex;
final int prevCycles = this.prevCycles;
final int prevTakeIndex = this.prevTakeIndex;
//cycles和takeIndex存在不一致,需要修正
if (cycles != prevCycles || takeIndex != prevTakeIndex) {
final int len = items.length;
// how far takeIndex has advanced since the previous
// operation of this iterator
//計算出隊了元素數量
long dequeues = (cycles - prevCycles) * len
+ (takeIndex - prevTakeIndex);
// Check indices for invalidation
//校驗下標合法性,lastRet 和 nextIndex 無效記錄被移除,cursor無效那麼下一個next讀取從takeIndex重新開始
if (invalidated(lastRet, prevTakeIndex, dequeues, len))
lastRet = REMOVED;
if (invalidated(nextIndex, prevTakeIndex, dequeues, len))
nextIndex = REMOVED;
if (invalidated(cursor, prevTakeIndex, dequeues, len))
cursor = takeIndex;
//進入detach模式
if (cursor < 0 && nextIndex < 0 && lastRet < 0)
detach();
else {
//記錄新的cycles和takeIndex
this.prevCycles = cycles;
this.prevTakeIndex = takeIndex;
}
}
}
//判斷下標合法性,無效返回true
//判斷邏輯,下標距離的prevTakeIndex元素數量 和 出隊元素數量 比較
private boolean invalidated(int index, int prevTakeIndex,
long dequeues, int length) {
if (index < 0)
return false;
int distance = index - prevTakeIndex;
if (distance < 0)
distance += length;
return dequeues > distance;
}
用於檢查並修正Itr的下標屬性,在noNext、next和remove方法中會被調用。
7.4 Itr#detach()
private void detach() {
// Switch to detached mode
// assert lock.getHoldCount() == 1;
// assert cursor == NONE;
// assert nextIndex < 0;
// assert lastRet < 0 || nextItem == null;
// assert lastRet < 0 ^ lastItem != null;
if (prevTakeIndex >= 0) {
// assert itrs != null;
prevTakeIndex = DETACHED;
// try to unlink from itrs (but not too hard)
itrs.doSomeSweeping(true);
}
}
修改detach的標誌字段,並且啓動itrs的清理邏輯。
7.5 Itr#hasNext()
public boolean hasNext() {
// assert lock.getHoldCount() == 0;
if (nextItem != null)
return true;
noNext();
return false;
}
private void noNext() {
final ReentrantLock lock = ArrayBlockingQueue.this.lock;
lock.lock();
try {
// assert cursor == NONE;
// assert nextIndex == NONE;
if (!isDetached()) {
// assert lastRet >= 0;
incorporateDequeues(); // might update lastRet
if (lastRet >= 0) {
lastItem = itemAt(lastRet);
// assert lastItem != null;
detach();
}
}
// assert isDetached();
// assert lastRet < 0 ^ lastItem != null;
} finally {
lock.unlock();
}
}
判斷下一個節點是否存在,由於下一個元素已經預讀取並保存在nextItem,判斷nextItem是否爲null即可。
對於沒有下一個節點的迭代器,需要修正下標屬性並進入detach模式。
7.6 Itr#next()
public E next() {
// assert lock.getHoldCount() == 0;
final E x = nextItem;
if (x == null)
throw new NoSuchElementException();
final ReentrantLock lock = ArrayBlockingQueue.this.lock;
lock.lock();
try {
if (!isDetached())
incorporateDequeues();
// assert nextIndex != NONE;
// assert lastItem == null;
lastRet = nextIndex;
final int cursor = this.cursor;
if (cursor >= 0) {
nextItem = itemAt(nextIndex = cursor);
// assert nextItem != null;
this.cursor = incCursor(cursor);
} else {
nextIndex = NONE;
nextItem = null;
}
} finally {
lock.unlock();
}
return x;
}
由於下一個元素已經與讀取,因此next方法的主要邏輯不是讀取下一個元素,而是預讀取並保存下一個元素的下一個元素。
7.7 Itr#remove()
public void remove() {
// assert lock.getHoldCount() == 0;
final ReentrantLock lock = ArrayBlockingQueue.this.lock;
lock.lock();
try {
//非detach模式修正下標屬性
if (!isDetached())
incorporateDequeues(); // might update lastRet or detach
final int lastRet = this.lastRet;
//lastRest清空
this.lastRet = NONE;
if (lastRet >= 0) {
//非detach模式直接移除
if (!isDetached())
removeAt(lastRet);
else {
//detach需要判斷lastItem是否發生了改變,才能移除
final E lastItem = this.lastItem;
// assert lastItem != null;
this.lastItem = null;
if (itemAt(lastRet) == lastItem)
removeAt(lastRet);
}
} else if (lastRet == NONE)
throw new IllegalStateException();
// else lastRet == REMOVED and the last returned element was
// previously asynchronously removed via an operation other
// than this.remove(), so nothing to do.
if (cursor < 0 && nextIndex < 0)
//進入detach模式
detach();
} finally {
lock.unlock();
// assert lastRet == NONE;
// assert lastItem == null;
}
}
7.8 Itr#removeAt(int)
boolean removedAt(int removedIndex) {
// assert lock.getHoldCount() == 1;
if (isDetached())
return true;
final int cycles = itrs.cycles;
final int takeIndex = ArrayBlockingQueue.this.takeIndex;
final int prevCycles = this.prevCycles;
final int prevTakeIndex = this.prevTakeIndex;
final int len = items.length;
int cycleDiff = cycles - prevCycles;
if (removedIndex < takeIndex)
cycleDiff++;
final int removedDistance =
(cycleDiff * len) + (removedIndex - prevTakeIndex);
// assert removedDistance >= 0;
int cursor = this.cursor;
if (cursor >= 0) {
int x = distance(cursor, prevTakeIndex, len);
if (x == removedDistance) {
if (cursor == putIndex)
this.cursor = cursor = NONE;
}
else if (x > removedDistance) {
// assert cursor != prevTakeIndex;
this.cursor = cursor = dec(cursor);
}
}
int lastRet = this.lastRet;
if (lastRet >= 0) {
int x = distance(lastRet, prevTakeIndex, len);
if (x == removedDistance)
this.lastRet = lastRet = REMOVED;
else if (x > removedDistance)
this.lastRet = lastRet = dec(lastRet);
}
int nextIndex = this.nextIndex;
if (nextIndex >= 0) {
int x = distance(nextIndex, prevTakeIndex, len);
if (x == removedDistance)
this.nextIndex = nextIndex = REMOVED;
else if (x > removedDistance)
this.nextIndex = nextIndex = dec(nextIndex);
}
else if (cursor < 0 && nextIndex < 0 && lastRet < 0) {
this.prevTakeIndex = DETACHED;
return true;
}
return false;
}
所有隊列移除非takeIndex下標處元素的方法都會調用迭代器的removeAt方法以通知其修正下標索引值。
7.9 Itr#takeIndexWrapped()
boolean takeIndexWrapped() {
// assert lock.getHoldCount() == 1;
if (isDetached())
return true;
//已經循環至少兩圈,迭代器的所有元素都已經無效
if (itrs.cycles - prevCycles > 1) {
// All the elements that existed at the time of the last
// operation are gone, so abandon further iteration.
shutdown();
return true;
}
return false;
}
void shutdown() {
// assert lock.getHoldCount() == 1;
cursor = NONE;
if (nextIndex >= 0)
nextIndex = REMOVED;
if (lastRet >= 0) {
lastRet = REMOVED;
lastItem = null;
}
prevTakeIndex = DETACHED;
// Don't set nextItem to null because we must continue to be
// able to return it on next().
//
// Caller will unlink from itrs when convenient.
}
當takeIndex循環到0時調用,對迭代器做一次檢查,如果必要則進入detach狀態,返回迭代器是否是detach狀態。
可以看到,進入detach模式雖然有兩種情況,標誌字段爲prevTakeIndex。
(1)所有索引都爲負數
(2)hasNext第一次返回false,關鍵是cursor < 0。
所以進入detach模式的關鍵有3種情況:
(1)cursor == putIndex,這時候cursor =NONE
(2)空隊列
(3)cycle - preCycles > 1