十、并发队列之ArrayBlockingQueue源码解析

类结构

  • final Object[] items; // 队列元素
  • int takeIndex; // 出队索引
  • int putIndex; // 入队索引
  • int count; // 元素数量,注意并不是volatile或原子类
  • ReentrantLock lock; // 独占锁
  • Condition notEmpty; // 出队锁
  • Condition notFull; // 入队锁

从这些属性我们大概就能知道以下:

  1. 入队和出队用的同一把锁,也就是同一时刻只有一个线程能获取
  2. 使用数组实现队列,也就不再使用内部Node类了,这会一定程度节省空间

    方法

    只要是队列还是那些方法offer、poll、peek等,而和LinkedBlockingQueue一样,put和take是一个阻塞的方法,直到插入成功。这里不再赘述,有兴趣的可以看上一篇文章。
  • offer
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    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();
    }
    }
    代码很简单,我们重点关注enqueue(e)入队方法
  • enqueue
    1
    2
    3
    4
    5
    6
    7
    8
    private void enqueue(E x) {
    final Object[] items = this.items;
    items[putIndex] = x;
    if (++putIndex == items.length) // 如果已经是最后索引了,则下标改为0
    putIndex = 0;
    count++;
    notEmpty.signal();
    }
    dequeue出队操作也类似,从上面代码中可以看到,它是使用了putIndex、takeIndex对数组的循环操作,来实现队列。
  • 其他方法类似size,contains方法都是使用锁,所以也都是精确的。