BlockingQueue子類之ArrayBlockingQueue
Interface BlockingQueue<E>
public interface BlockingQueue<E> extends Queue<E>
BlockingQueue是Queue的子類,具有Queue的性質。
它實現了幾個主要的方法:
1.add(E e)
將特定的元素插入隊列,如果隊列的大小沒有達到隊列空間的上限則插入成功,就返回True,否則拋出異常。
2.offer(E e)
和add一樣,不過當隊列大小達到上限時不會拋出錯誤,而是返回False。
3.offer(E e,long timeout,TimeUnit unit)
和offer(E e)一樣,不過給了一定的等待時間,不像offer(E e)一樣沒有空間就立即返回False,只要在timeout時間之內能夠插入隊列,就返回True。
4.poll(long timeout, TimeUnit unit)
刪除並返回隊列的頭部,限制時間。
5.put(E e)
將指定元素放入隊列,可以等待直到有空閒空間。
6.remove(Object o)
如果隊列中存在這個對象的話就把它刪除掉
BlockingQueue有許多子類。
像ArrayBlockdingQueue DelauQueue LinkedBlockingQueue LinkedBlockingDeque
LinkedTransferQueue PriorityBlockingQueue SynchronousQueue
我們介紹幾個常用的實現類。
1.ArrayBlockingQueue
public ArrayBlockingQueue(int capacity) {
this(capacity, false);
}
/**
* Creates an {@code ArrayBlockingQueue} with the given (fixed)
* capacity and the specified access policy.
*
* @param capacity the capacity of this queue
* @param fair if {@code true} then queue accesses for threads blocked
* on insertion or removal, are processed in FIFO order;
* if {@code false} the access order is unspecified.
* @throws IllegalArgumentException if {@code capacity < 1}
*/
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有兩個構造函數,capacity是這個隊列的大小,當我們不指定fair時,默認是unfair,即不遵守先進先出原則。
ArrayBlockingQueue底層是基於數組實現的。
/** 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;
三把鎖
/** Main lock guarding all access */
final ReentrantLock lock;
/** Condition for waiting takes */
private final Condition notEmpty;
/** Condition for waiting puts */
private final Condition notFull;
lock是全局鎖,在常用的方法上都加了ReentrantLock。
Condition鎖主要是爲了線程間通信所設的鎖,用來喚醒在等待獲取鎖的線程。
常用的方法源碼
add和offer
public boolean add(E e) {
return super.add(e);
}
public boolean offer(E e) {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lock(); // 一直等到獲取鎖
try {
if (count == items.length)
return false;
else {
enqueue(e);
return true;
}
} finally {
lock.unlock();
}
}
put
public void put(E e) throws InterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly(); //可中斷的獲取鎖
try {
while (count == items.length) //當線程從等待中被喚醒時,會比較當前隊列是否已經滿了
notFull.await(); //notFull = lock.newCondition 表示隊列不滿這種狀況,假如現場在插入的時候
enqueue(e); //當前隊列已經滿了時,則需要等到這種情況的發生。
} finally { //可以看出這是一種阻塞式的插入方式
lock.unlock();
}
}
take
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)
notEmpty.await(); //notEmpty=lock.newCondition表示隊列不爲空的這種情況。假如一個線程進行take
return dequeue(); //操作時,隊列爲空,則會一直等到到這種情況發生。
} finally {
lock.unlock();
}
}
peek
public E peek() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return itemAt(takeIndex); // null when queue is empty
enqueueprivate void enqueue(E x) { //因爲調用enqueue的方法都已經同步過了,這裏就不需要在同步了
// assert lock.getHoldCount() == 1;
// assert items[putIndex] == null;
final Object[] items = this.items;
items[putIndex] = x; //putIndex是下一個放至元素的座標
if (++putIndex == items.length) //putIndex+1, 並且比較是否與數組長度相同,是的話,則從數組開頭
putIndex = 0; //插入元素,這就是循環數組的奧祕了
count++; //當前元素總量+1
notEmpty.signal(); //給等到在數組非空的線程一個信號,喚醒他們。
}
dequeueprivate E dequeue() {
// assert lock.getHoldCount() == 1;
// assert items[takeIndex] != null;
final Object[] items = this.items;
@SuppressWarnings("unchecked")
E x = (E) items[takeIndex];
items[takeIndex] = null; //將要取出的元素指向null 表示這個元素已經取出去了
if (++takeIndex == items.length) //takeIndex +1,同樣的假如已經取到了數組的末尾,那麼就要重新開始取
takeIndex = 0; //這就是循環數組
count--;
if (itrs != null)
itrs.elementDequeued(); //這裏實現就比較麻煩,下次單獨出一個吧,可以看看源碼
notFull.signal(); //同樣 需要給 等待數組不滿這種情況的線程一個信號,喚醒他們。
return x;
}
循環數組大大降低了普通數組需要調整元素位置所耗費的時間。