一、簡介
BlockingQueue介面定義了一種阻塞的FIFO queue,每一個BlockingQueue都有一個容量,讓容量滿時往BlockingQueue中添加資料時會造成阻塞,當容量為空白時取元素操作會阻塞。ArrayBlockingQueue是對BlockingQueue的一個數組實現,它使用一把全域的鎖並行對queue的讀寫操作,同時使用兩個Condition阻塞容量為空白時的取操作和容量滿時的寫操作。
二、具體實現
ArrayBlockingQueue底層定義如下:
public class ArrayBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, java.io.Serializable { // 使用迴圈數組來實現queue,初始時takeIndex和putIndex均為0 private final E[] items; private transient int takeIndex; private transient int putIndex; private int count; // 用於並發的鎖和條件 private final ReentrantLock lock; private final Condition notEmpty; private final Condition notFull; /** * 迴圈數組 * Circularly increment i. */ final int inc(int i) { return (++i == items.length)? 0 : i; } public ArrayBlockingQueue(int capacity, boolean fair) { if (capacity <= 0) throw new IllegalArgumentException(); this.items = (E[]) new Object[capacity]; // 分配鎖及該鎖上的condition lock = new ReentrantLock(fair); notEmpty = lock.newCondition(); notFull = lock.newCondition(); } ... }
ArrayBlockingQueue的取操作:
public class ArrayBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, java.io.Serializable { private E extract() { final E[] items = this.items; E x = items[takeIndex]; items[takeIndex] = null; takeIndex = inc(takeIndex); --count; // 激發notFull條件 notFull.signal(); return x; } /** * condition的await的語義如下: * 與condition相關的鎖以原子方式釋放,並禁用該線程 * 方法返回時,線程必須獲得與該condition相關的鎖 */ public E take() throws InterruptedException { final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { try { // 等待notEmpty的條件 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(); } } ... }
ArrayBlockingQueue的寫操作:
public class ArrayBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, java.io.Serializable { private void insert(E x) { items[putIndex] = x; putIndex = inc(putIndex); ++count; notEmpty.signal(); } public void put(E o) throws InterruptedException { if (o == null) throw new NullPointerException(); final E[] items = this.items; final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { try { // 等待notFull條件 while (count == items.length) notFull.await(); } catch (InterruptedException ie) { notFull.signal(); // propagate to non-interrupted thread throw ie; } insert(o); } finally { lock.unlock(); } } ... }
注意:ArrayBlockingQueue在讀寫操作上都需要鎖住整個容器,因此輸送量與一般的實現是相似的,適合於實現“生產者消費者”模式。