ArrayBlockingQueue 是一个由数组支持的有界阻塞队列。此队列按 FIFO(先进先出)原则对元素进行排序。
BlockingQueue 接口实现的都是阻塞队列。而 ConcurrentLinkedQueue 是基于 CAS 和链表实现的无阻塞高性能队列。
ConcurrentLinkedQueue 的源码分析见 Java并发Concurrent包——ConcurrentLinkedQueue源码分析
这是一个典型的“有界缓存区”,固定大小的数组在其中保持生产者插入的元素和使用者提取的元素。一旦创建了这样的缓存区,就不能再增加其容量。试图向已满队列中放入元素会导致操作受阻塞;试图从空队列中提取元素将导致类似阻塞。
属性
// 底层对象数组
final Object[] items;
// 出队的下标
int takeIndex;
// 入队的下标
int putIndex;
// 元素个数
int count;
// 锁对象,控制所有访问
final ReentrantLock lock;
// 出队的condition
private final Condition notEmpty;
// 入队的condition
private final Condition notFull;
// 迭代器
transient Itrs itrs = null;
构造函数
列出一个最主要的构造函数来分析:
public ArrayBlockingQueue(int capacity, boolean fair) {
if (capacity <= 0)
throw new IllegalArgumentException();
// 初始化底层数组
this.items = new Object[capacity];
// 新建公平或非公平锁,和两个condition
lock = new ReentrantLock(fair);
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
重要方法
offer(E e)
public boolean offer(E e) {
// 判断对象不为null
checkNotNull(e);
// 锁对象
final ReentrantLock lock = this.lock;
// 加锁
lock.lock();
try {
if (count == items.length)
// 如果数组已经满了,不能再入队
return false;
else {
// 调用enqueue方法,下面分析
enqueue(e);
return true;
}
} finally {
lock.unlock();
}
}
enqueue(E x)
// 只在当前线程拥有锁的时候调用
private void enqueue(E x) {
final Object[] items = this.items;
// 在入队下标处放入新元素
items[putIndex] = x;
// 下标加1后,判断是否等于数组总长度,如果是将putIndex置为0
if (++putIndex == items.length)
putIndex = 0;
// 有效元素个数加1
count++;
// 唤醒其他基于此condition的线程
notEmpty.signal();
}
poll()
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
// 有效元素不为0就可以调用dequeue方法出列
return (count == 0) ? null : dequeue();
} finally {
lock.unlock();
}
}
dequeue()
// 只在当前线程拥有锁的时候调用
private E dequeue() {
final Object[] items = this.items;
@SuppressWarnings("unchecked")
// 保存出队下标处的元素
E x = (E) items[takeIndex];
// 将该下标处置为null
items[takeIndex] = null;
// 如果也走到尽头,从0开始
if (++takeIndex == items.length)
takeIndex = 0;
// 有效元素个数减1
count--;
//
if (itrs != null)
itrs.elementDequeued();
// 队列不再满,发信号给其他线程
notFull.signal();
// 返回出队的对象
return x;
}
take()
获取并移除此队列的头部,在元素变得可用之前一直等待(如果有必要)。
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
// 如果可用元素为空,则等待notEmpty这个condition的信号
// 直到出现不为空时,进行出队操作
while (count == 0)
notEmpty.await();
return dequeue();
} finally {
lock.unlock();
}
}
put(E e)
将指定的元素插入此队列的尾部,如果该队列已满,则等待可用的空间。和 take 方法对应。
public void put(E e) throws InterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
// 如果元素已满,则线程阻塞等待出现notFull的情况,才能入队
while (count == items.length)
notFull.await();
enqueue(e);
} finally {
lock.unlock();
}
}
该类的代码比较简单易懂。