跳转至

ArrayBlockingQueue:基于数组的有界队列

一、是什么

基于数组的有界阻塞队列,必须指定容量。

BlockingQueue<String> q = new ArrayBlockingQueue<>(100);

二、结构

items: [e1, e2, e3, null, null]
       putIndex=0     takeIndex=2
  • 数组 items。
  • putIndex:下一个写位置。
  • takeIndex:下一个读位置。
  • count:当前元素数。

三、单锁设计

和 LinkedBlockingQueue 不同,ArrayBlockingQueue 用一个 ReentrantLock:

public class ArrayBlockingQueue<E> {
    final Object[] items;
    int takeIndex;
    int putIndex;
    int count;

    final ReentrantLock lock;
    private final Condition notEmpty;
    private final Condition notFull;
}

读和写共用一把锁,所以读写互斥。

四、put 流程

public void put(E e) throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        while (count == items.length)
            notFull.await();
        enqueue(e);
    } finally {
        lock.unlock();
    }
}

private void enqueue(E x) {
    items[putIndex] = x;
    if (++putIndex == items.length) putIndex = 0;   // 循环
    count++;
    notEmpty.signal();
}

五、为什么用单锁

  • 数组实现简单,读写在同一个数组上,双锁很难实现。
  • 性能不如 LinkedBlockingQueue,但内存开销小(没有 Node 对象)。

六、公平性可选

new ArrayBlockingQueue<>(100);        // 非公平
new ArrayBlockingQueue<>(100, true);  // 公平

公平锁按等待顺序,吞吐低。

七、对比 LinkedBlockingQueue

ArrayBlockingQueue LinkedBlockingQueue
单锁 读写双锁
读写并行
内存 连续数组 Node 对象
容量 必须指定 默认无界
GC 无额外对象 有 Node

怎么选

  • 吞吐优先:LinkedBlockingQueue。
  • 内存敏感、容量固定:ArrayBlockingQueue。