[JDK1.6] JAVA集合 ArrayBlockingQueue 源碼淺析 (同步/阻塞隊列)

註釋來自java api
源碼來自 JDK1.6

[一] 簡介:

一個由數組支持的有界阻塞隊列。此隊列按 FIFO(先進先出)原則對元素進行排序。隊列的頭部 是在隊列中存在時間最長的元素。隊列的尾部 是在隊列中存在時間最短的元素。新元素插入到隊列的尾部,隊列獲取操作則是從隊列頭部開始獲得元素。

這是一個典型的“有界緩存區”,固定大小的數組在其中保持生產者插入的元素和使用者提取的元素。一旦創建了這樣的緩存區,就不能再增加其容量。試圖向已滿隊列中放入元素會導致操作受阻塞;試圖從空隊列中提取元素將導致類似阻塞。

此類支持對等待的生產者線程和使用者線程進行排序的可選公平策略。默認情況下,不保證是這種排序。然而,通過將公平性 (fairness) 設置爲 true 而構造的隊列允許按照 FIFO 順序訪問線程。公平性通常會降低吞吐量,但也減少了可變性和避免了“不平衡性”。

此類及其迭代器實現了 Collection 和 Iterator 接口的所有可選 方法。

此類是 Java Collections Framework 的成員。

注意: ArrayBlockingQueue 的容量一旦初始化大小將不會改變, 即不會有擴容的策略
架構

public class ArrayBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable {
        //.......
 }

在這裏插入圖片描述

[二] 關鍵屬性:

屬性 說明
items 存放元素隊列(數組)
takeIndex 下一個取出元素的索引
putIndex 下一個存放元素的索引
count 所有元素的數量
lock 線程鎖, 用來控制獲取和存放的操作
notEmpty 用於取出元素時,如果隊列沒有元素,讓線程等待釋放鎖的對象(進入阻塞狀態)
notFull 用於存入元素時,如果線程滿了, 讓線程等待釋放鎖的對象 (進入阻塞狀態)
    /** 存放元素隊列 */
    private final E[] items;
    /** 下一個元素取出的索引, 方法爲 take, poll or remove */
    private int takeIndex;
    /** 下一個元素存放的索引, 方法爲 put, offer, or add. */
    private int putIndex;
    /** 隊列元素數量 */
    private int count;

    /*
     * Concurrency control uses the classic two-condition algorithm
     * found in any textbook.
     */

    /** lock, 控制所有的訪問*/
    private final ReentrantLock lock;
    /** Condition 對應等待獲取*/
    private final Condition notEmpty;
    /** Condition 對應等待放入*/
    private final Condition notFull;

[三] 初始化

源碼註釋: 創建一個帶有給定的(固定)容量和默認訪問策略的 ArrayBlockingQueue。

    public ArrayBlockingQueue(int capacity) {
        this(capacity, false);
    }

源碼註釋: 創建一個具有給定的(固定)容量和指定訪問策略的 ArrayBlockingQueue。
fair - 如果爲 true,則按照 FIFO 順序訪問插入或移除時受阻塞線程的隊列;如果爲 false,則訪問順序是不確定的。

    public ArrayBlockingQueue(int capacity, boolean fair) {
        if (capacity <= 0)
            throw new IllegalArgumentException();
        this.items = (E[]) new Object[capacity];
        lock = new ReentrantLock(fair);
        notEmpty = lock.newCondition();
        notFull =  lock.newCondition();
    }

源碼註釋: 創建一個具有給定的(固定)容量和指定訪問策略的 ArrayBlockingQueue,它最初包含給定 collection 的元素,並以 collection 迭代器的遍歷順序添加元素。

    public ArrayBlockingQueue(int capacity, boolean fair, Collection<? extends E> c) {
        this(capacity, fair);
        if (capacity < c.size())
            throw new IllegalArgumentException();

        for (Iterator<? extends E> it = c.iterator(); it.hasNext();)
            add(it.next());
    }

[四] ArrayBlockingQueue 核心方法

插入元素

源碼註釋: 在當前放置位置,前進和信號處插入元素。
只有獲取了鎖,才能調用。

    private void insert(E x) {
        items[putIndex] = x;
        putIndex = inc(putIndex);
        ++count;
        notEmpty.signal();
    }

循環增量

    final int inc(int i) {
        return (++i == items.length)? 0 : i;
    }

取出元素

源碼註釋: 在當前獲取位置,前進和信號處提取元素。
只有獲取了鎖,才能調用。

    private E extract() {
        final E[] items = this.items;
        E x = items[takeIndex];
        items[takeIndex] = null;
        takeIndex = inc(takeIndex);
        --count;
        notFull.signal();
        return x;
    }

(阻塞) put

當隊列滿 full 時, 進入阻塞狀態

    public void put(E e) throws InterruptedException {
        if (e == null) throw new NullPointerException();
        final E[] items = this.items;
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            try {
                while (count == items.length)
                    notFull.await();
            } catch (InterruptedException ie) {
                notFull.signal(); // propagate to non-interrupted thread
                throw ie;
            }
            insert(e);
        } finally {
            lock.unlock();
        }
    }

(阻塞) take

當隊列爲空isEmpty 時, 進入阻塞狀態

    public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            try {
                while (count == 0)
                    notEmpty.await();
            } catch (InterruptedException ie) {
                notEmpty.signal(); // propagate to non-interrupted thread
                throw ie;
            }
            E x = extract();
            return x;
        } finally {
            lock.unlock();
        }
    }

(非阻塞) offer(E)

源碼註釋: 將指定的元素插入到此隊列的尾部(如果立即可行且不會超過該隊列的容量),在成功時返回 true,如果此隊列已滿,則返回 false。

    public boolean offer(E e) {
        if (e == null) throw new NullPointerException();
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            if (count == items.length)
                return false;
            else {
                insert(e);
                return true;
            }
        } finally {
            lock.unlock();
        }
    }

(非阻塞) poll()

源碼註釋: 獲取並移除此隊列的頭,如果此隊列爲空,則返回 null。

    public E poll() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            if (count == 0)
                return null;
            E x = extract();
            return x;
        } finally {
            lock.unlock();
        }
    }

線程的等待與喚醒

調用圖
在這裏插入圖片描述

  1. 在調用 put方法時, 如果隊列滿了, 會調用 notFull.await() 方法讓線程進入等待狀態;
  2. 往隊列存放一個元素時,最終調用 insert() ,完成後會喚醒一個線程, notEmpty.signal();
  3. 在調用 take方法時, 如果隊列爲空, 會調用 notEmpty.await() 方法讓線程進入等待狀態;
  4. 從隊列取出一個元素是, 最終調用 extract()方法, 完成後會喚醒一個線程, notFull.signal();
  5. put 等待的線程 notFull.await(),由 extract() 方法喚醒 notFull.signal();
  6. take 等待的線程 notEmpty.await(), 由 insert() 方法喚醒 notEmpty.signal();

迭代器 Iterator

iterator 返回迭代器

    public Iterator<E> iterator() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return new Itr();
        } finally {
            lock.unlock();
        }
    }

Itr 迭代器實現類

private class Itr implements Iterator<E> {

        private int remaining; // 尚未返回的元素數量
        private int nextIndex; // 下一個要返回的元素的索引
        private E nextItem;    // 下一次調用將返回的元素
        private E lastItem;    // 最後一次調用返回的元素
        private int lastRet;   // 返回的最後一個元素的索引,如果沒有則返回-1

        Itr() {
            lastRet = -1;
            if ((remaining = count) > 0)
                nextItem = items[nextIndex = takeIndex];
        }
        
        public boolean hasNext() {
            return remaining > 0;
        }
        
        public E next() {
            if (remaining <= 0)
                throw new NoSuchElementException();
            final ReentrantLock lock = ArrayBlockingQueue.this.lock;
            lock.lock();
            try {
                lastRet = nextIndex;
                E x = lastItem = nextItem;
                while (--remaining > 0) {
                    if ((nextItem = items[nextIndex = inc(nextIndex)]) != null)
                        break;
                }
                return x;
            } finally {
                lock.unlock();
            }
        }
        
        public void remove() {
            final ReentrantLock lock = ArrayBlockingQueue.this.lock;
            lock.lock();
            try {
                int i = lastRet;
                if (i == -1)
                    throw new IllegalStateException();
                lastRet = -1;
                E x = lastItem;
                lastItem = null;
                // only remove if item still at index
                if (x == items[i]) {
                    boolean removingHead = (i == takeIndex);
                    removeAt(i);
                    if (!removingHead)
                        nextIndex = dec(nextIndex);
                }
            } finally {
                lock.unlock();
            }
        }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章