ArrayBlockingQueue原始碼講解
阿新 • • 發佈:2018-12-12
尊重原創,轉載請標明出處 http://blog.csdn.net/abcdef314159
原始碼:\sources\android-25
ArrayBlockingQueue是一個數組阻塞佇列,這個佇列的元素是先進先出,head元素是最先加入的,tail是最後加入的,並且新的元素加入到tail,獲取元素從head開始。他有兩個int指標,putIndex指向隊尾的下一個,是空,表示下一個存放的位置,takeInput指向隊首,表示下一個讀取的位置takeInput有可能大於putIndex,也有可能小於putIndex。takeInput有固定大小,一旦建立大小則不能改變,如果把一個元素放入到一個滿的佇列中則會阻塞,同樣如果從一個空的佇列中獲取元素也會阻塞。先看一下ArrayBlockingQueue的幾個方法,從上往下看,先看第一個dec(int i)
/**
* Circularly decrements array index i.
*/
final int dec(int i) {
//他表示獲取當前下標i的前一個下標,如果i為0就表示獲取head的前一個,
//這裡讓他等於最後一個。
return ((i == 0) ? items.length : i) - 1;
}
接著看下一個方法enqueue(E x),表示加入一個元素x,
接著下一個方法dequeue()表示移除第一個元素,注意第一個元素是takeIndex下標的元素,最後一個元素是putIndex下標的上一個元素,putIndex指向的是下一個存放的位置。接著看下一個方法removeAt(final int removeIndex),移除指定位置的元素/** * Inserts element at current put position, advances, and signals. * Call only when holding lock. */ private void enqueue(E x) { // assert lock.getHoldCount() == 1; // assert items[putIndex] == null; final Object[] items = this.items; //putIndex表示最後插入元素的index items[putIndex] = x; //如果puIndex等於items的長度,則讓putIndex等於0, if (++putIndex == items.length) putIndex = 0; count++; notEmpty.signal(); }
接著看下一個remove(Object o)方法/** * Deletes item at array index removeIndex. * Utility for remove(Object) and iterator.remove. * Call only when holding lock. */ void removeAt(final int removeIndex) { // assert lock.getHoldCount() == 1; // assert items[removeIndex] != null; // assert removeIndex >= 0 && removeIndex < items.length; final Object[] items = this.items; if (removeIndex == takeIndex) { //如果移除的正好等於頭元素,直接移除,因為這個佇列就是先進先出的。 // removing front item; just advance items[takeIndex] = null; //如果takeIndex等於items的長度,則讓他等於0,相當於從隊尾又指向隊首 if (++takeIndex == items.length) takeIndex = 0; count--; if (itrs != null) itrs.elementDequeued(); } else { // an "interior" remove // slide over all others up through putIndex. for (int i = removeIndex, putIndex = this.putIndex;;) { int pred = i; if (++i == items.length) i = 0; //打算把後面的往前移,直到找到putIndex才會break,如果後面一個正好是putIndex //(putIndex相當於隊尾,就是元素存放的位置,實際上還沒有存,是空的),則直接 //把當前下標為i的刪除,然後再讓putIndex等於i, if (i == putIndex) { items[pred] = null; this.putIndex = pred; break; } //把後面的往前移 items[pred] = items[i]; } count--; if (itrs != null) itrs.removedAt(removeIndex); } notFull.signal(); }
public boolean remove(Object o) {
if (o == null) return false;
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count > 0) {
final Object[] items = this.items;
//putIndex相當於尾
final int putIndex = this.putIndex;
//takeIndex相當於頭,但要注意,putIndex和takeIndex相當於
//兩個指標,有可能putIndex大於takeIndex,也有可能takeIndex大於putIndex
int i = takeIndex;
//從takeIndex開始檢查是否相等,如果相等則直接返回,否則迴圈檢查,直到
//i等於putIndex為止,因為takeIndex是最先加入的,元素的範圍也就是從
// takeIndex到putIndex,這裡takeIndex和putIndex誰大誰小還不一定,
//這裡可以參照前面寫的《ArrayDeque原始碼詳解》
do {
if (o.equals(items[i])) {
removeAt(i);
return true;
}
if (++i == items.length) i = 0 ;//如果找到陣列的最後,要從頭開始
} while (i != putIndex);
}
return false;
} finally {
lock.unlock();
}
}
接著是contains(Object o)方法,和上面的contains(Object o)方法差不多,就不在介紹。接著是Object[] toArray()方法,這裡要分兩種情況,一種是putIndex大於takeIndex,還一種是putIndex小於takeIndex。這個可以參照前面講的
《ArrayDeque原始碼詳解》的Object[] toArray()方法,這裡就不在介紹。再來看最後一個方法drainTo
public int drainTo(Collection<? super E> c, int maxElements) {
//從takeIndex開始提取maxElements個元素儲存在集合c中,如果maxElements
//大於陣列,則提取大小為items.size();提取之後把當前的ArrayBlockingQueue
//中提取的刪除。
Objects.requireNonNull(c);
if (c == this)
throw new IllegalArgumentException();
if (maxElements <= 0)
return 0;
final Object[] items = this.items;
final ReentrantLock lock = this.lock;
lock.lock();
try {
int n = Math.min(maxElements, count);
int take = takeIndex;
int i = 0;
try {
while (i < n) {
@SuppressWarnings("unchecked")
E x = (E) items[take];
c.add(x);
items[take] = null;
if (++take == items.length) take = 0;
i++;
}
return n;
} finally {
// Restore invariants even if c.add() threw
if (i > 0) {
count -= i;
takeIndex = take;
if (itrs != null) {
if (count == 0)
itrs.queueIsEmpty();
else if (i > take)
itrs.takeIndexWrapped();
}
for (; i > 0 && lock.hasWaiters(notFull); i--)
notFull.signal();
}
}
} finally {
lock.unlock();
}
}