ArrayBlockingQueue源码分析

ArrayBlockingQueue底层是使用一个数组实现队列的,并且在构造ArrayBlockingQueue时需要指定容量,也就意味着底层数组一旦创建了,容量就不能改变了,因此ArrayBlockingQueue是一个容量限制的阻塞队列。因此,在队列全满时执行入队将会阻塞,在队列为空时出队同样将会阻塞。

ArrayBlockingQueue的重要字段有如下几个:

/** The queued items */
 final Object[] items;
/** Main lock guarding all access */
final ReentrantLock lock;
/** Condition for waiting takes */ 
private final Condition notEmpty;
/** Condition for waiting puts */ 
private final Condition notFull;

一、put(E e)方法

put(E e)方法在队列不满的情况下,将会将元素添加到队列尾部,如果队列已满,将会阻塞,直到队列中有剩余空间可以插入。该方法的实现如下:

/**
 * Inserts the specified element at the tail of this queue, waiting
 * for space to become available if the queue is full.
 *
 * @throws InterruptedException {@inheritDoc}
 * @throws NullPointerException {@inheritDoc}
 */
public void put(E e) throws InterruptedException {
    Objects.requireNonNull(e);
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        while (count == items.length)
            notFull.await();
        enqueue(e);
    } finally {
        lock.unlock();
    }
}

put方法总结:

  • 1. ArrayBlockingQueue不允许元素为null
  • 2. ArrayBlockingQueue在队列已满时将会调用notFull的await()方法释放锁并处于阻塞状态。
  • 3. 一旦ArrayBlockingQueue不为满的状态,就将元素入队。

二、E take()方法

take()方法用于取走队头的元素,当队列为空时将会阻塞,直到队列中有元素可取走时将会被释放。其实现如下:

public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        while (count == 0)
            notEmpty.await();
        return dequeue();
    } finally {
        lock.unlock();
    }
}

take方法总结:

一旦获得了锁之后,如果队列为空,那么将阻塞;否则调用dequeue()出队一个元素。

ArrayBlockingQueue总结:

ArrayBlockingQueue的并发阻塞是通过ReentrantLock和Condition来实现的,ArrayBlockingQueue内部只有一把锁,意味着同一时刻只有一个线程能进行入队或者出队的操作。

三、总结

在上一篇分析LinkedBlockingQueue的源码之后,可以与ArrayBlockingQueue做一个比较。

1、ArrayBlockingQueue

一个对象数组+一把锁+两个条件。入队与出队都用同一把锁,在只有入队高并发或出队高并发的情况下,因为操作数组,且不需要扩容,性能很高,采用了数组,必须指定大小,即容量有限。

2、LinkedBlockingQueue

一个单向链表+两把锁+两个条件。两把锁,一把用于入队,一把用于出队,有效的避免了入队与出队时使用一把锁带来的竞争。在入队与出队都高并发的情况下,性能比ArrayBlockingQueue高很多。

类似文章

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注