BlockingQueue子類之ArrayBlockingQueue

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
enqueue
private 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();			//給等到在數組非空的線程一個信號,喚醒他們。
}
dequeue

private 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;
}

循環數組大大降低了普通數組需要調整元素位置所耗費的時間。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章