目录
8、queueIsEmpty / elementDequeued / takeIndexWrapped / removedAt
ArrayBlockingQueue表示一个基于数组实现的固定容量的,先进先出的,线程安全的队列(栈),本篇博客就详细讲解该类的实现细节。
1、定义
ArrayBlockingQueue的类继承关系如下:
其核心接口BlockingQueue包含的方法定义如下:
上图中的Queue和Collection下面都有两个接口,这两个接口是BlockingQueue覆写的,并不是说这两类只有这两接口,Collection和Queue的主要接口实现都由AbstractQueue实现了,我们重点关注上图中列出来的这些接口的实现。
ArrayBlockingQueue包含的属性如下:
/**保存队列元素的数组 */
final Object[] items;
/** items index for next take, poll, peek or remove */
int takeIndex;
/** offer,add,put等方法将元素保存到该索引处 */
int putIndex;
/** 队列中元素的个数 */
int count;
/** 互斥锁*/
final ReentrantLock lock;
/** 如果数组是空的,在该Condition上等待 */
private final Condition notEmpty;
/** 如果数组是满的,在该Condition上等待 */
private final Condition notFull;
/** 遍历器实现 */
transient Itrs itrs = null;
重点关注以下方法的实现。
2、构造方法
public ArrayBlockingQueue(int capacity) {
this(capacity, false);
}
//capacity表示队列的容量,fair表示是否使用公平锁
public ArrayBlockingQueue(int capacity, boolean fair) {
if (capacity <= 0)
throw new IllegalArgumentException();
this.items = new Object[capacity];
lock = new ReentrantLock(fair);
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
public ArrayBlockingQueue(int capacity, boolean fair,
Collection<? extends E> c) {
this(capacity, fair);
final ReentrantLock lock = this.lock;
//加锁的目的是为了其他CPU能够立即看到修改
//加锁和解锁底层都是CAS,会强制修改写回主存,对其他CPU可见
lock.lock(); // Lock only for visibility, not mutual exclusion
try {
int i = 0;
try {
for (E e : c) {
checkNotNull(e);
items[i++] = e;
}
} catch (ArrayIndexOutOfBoundsException ex) {
throw new IllegalArgumentException();
}
count = i;
putIndex = (i == capacity) ? 0 : i;
} finally {
lock.unlock();
}
}
3、add / offer / put
这三个方法都是往队列中添加元素,说明如下:
- add方法依赖于offer方法,如果队列满了则抛出异常,否则添加成功返回true;
- offer方法有两个重载版本,只有一个参数的版本,如果队列满了就返回false,否则加入到队列中,返回true,add方法就是调用此版本的offer方法;另一个带时间参数的版本,如果队列满了则等待,可指定等待的时间,如果这期间中断了则抛出异常,如果等待超时了则返回false,否则加入到队列中返回true;
- put方法跟带时间参数的offer方法逻辑一样,不过没有等待的时间限制,会一直等待直到队列有空余位置了,再插入到队列中,返回true
public boolean add(E e) {
//调用子类offer方法实现,如果队列满了则抛出异常
return super.add(e);
}
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();
}
}
public void put(E e) throws InterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
//等待的过程被中断了则抛出异常
lock.lockInterruptibly();
try {
//如果数组是满的,则等待
while (count == items.length)
notFull.await();
//加入到数组中
enqueue(e);
} finally {
lock.unlock();
}
}
//同上面的put方法,只不过可以指定等待的时间
public boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException {
checkNotNull(e);
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
//数组满了则等待
while (count == items.length) {
if (nanos <= 0)
return false;
nanos = notFull.awaitNanos(nanos);
}
enqueue(e);
return true;
} finally {
lock.unlock();
}
}
public boolean add(E e) {
if (offer(e))
return true;
else
throw new IllegalStateException("Queue full");
}
private void enqueue(E x) {
final Object[] items = this.items;
//保存到putIndex索引处
items[putIndex] = x;
//数组满了,将其重置为0
if (++putIndex == items.length)
putIndex = 0;
//数组元素个数增加
count++;
//唤醒因为队列是空的而等待的线程
notEmpty.signal();
}
private static void checkNotNull(Object v) {
if (v == null)
throw new NullPointerException();
}
4、poll / take / peek
这几个方法都是获取队列顶的元素,具体说明如下:
- poll方法有两个重载版本,第一个版本,如果队列是空的,返回null,否则移除并返回队列头部元素;另一个带时间参数的版本,如果栈为空则等待,可以指定等待的时间,如果等待超时了则返回null,如果被中断了则抛出异常,否则移除并返回栈顶元素
- take方法同带时间参数的poll方法,但是不能指定等待时间,会一直等待直到队列中有元素为止,然后移除并返回栈顶元素
- peek方法只是返回队列头部元素,不移除
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
//如果为空,返回null,否则取出一个元素
return (count == 0) ? null : dequeue();
} finally {
lock.unlock();
}
}
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
//如果空的,则等待
while (count == 0)
notEmpty.await();
//取出一个元素
return dequeue();
} finally {
lock.unlock();
}
}
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
long nanos = unit.toNanos(timeout);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0) {
if (nanos <= 0)
return null; //等待超时,返回null
//数组为空,等待
nanos = notEmpty.awaitNanos(nanos);
}
return dequeue();
} finally {
lock.unlock();
}
}
public E peek() {
final ReentrantLock lock = this.lock;