简介
ArrayBlockingQueue是一个由数组结构组成的有界队列。此队列按照先进先出的顺序进行排序。支持公平锁和非公平锁,默认非公平锁。
#####ArrayBlockingQueue 类
public class ArrayBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable
继承AbstractQueue抽象类,并且实现BlockingQueue接口
ArrayBlockingQueue 属性
// 元素数组
final Object[] items;
// 下一次读取或移除的位置
int takeIndex;
// 下一次存放元素的位置
int putIndex;
// 元素个数
int count;
// 可重入锁
final ReentrantLock lock;
// 非空监控
private final Condition notEmpty;
// 非满监控
private final Condition notFull;
// 迭代器
transient Itrs itrs = null;
ArrayBlockingQueue 构造函数
public ArrayBlockingQueue(int capacity) {
this(capacity, false);
}
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;
lock.lock();
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();
}
}
ArrayBlockingQueue在构造时需要指定容量, 并可以选择是否需要公平性,如果公平参数被设置true,等待时间最长的线程会优先得到处理(其实就是通过将ReentrantLock设置为true来 达到这种公平性的:即等待时间最长的线程会先操作)。通常,公平性会使你在性能上付出代价,只有在的确非常需要的时候再使用它。它是基于数组的阻塞循环队 列,此队列按 FIFO(先进先出)原则对元素进行排序。
ArrayBlockingQueue 添加
public boolean add(E e) {
// 这里调用AbstractQueue中的add方法
// if (offer(e)) return true; else throw new IllegalStateException("Queue full");
return super.add(e);
}
AbstractQueue 中实际调用offer(E 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();
}
}
数组满了返回false,没满就去添加
public void put(E e) throws InterruptedException {
// 检查不为空
checkNotNull(e);
final ReentrantLock lock = this.lock;
// 获得支持响应中断的锁
lock.lockInterruptibly();
try {
// 使用while循环来判断队列是否已满,防止假唤醒
while (count == items.length)
// 如果队列满的话就阻塞等待,直到notFull的signal方法被调用,也就是队列里有空间了
notFull.await();
// 存入
enqueue(e);
} finally {
// 解锁
lock.unlock();
}
}
数组满了就等待
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) {
// 如果等待时间过了队列还是满的话就直接返回false,添加元素失败
if (nanos <= 0)
return false;
// 等待设置的时间
nanos = notFull.awaitNanos(nanos);
}
// 存入
enqueue(e);
return true;
} finally {
// 解锁
lock.unlock();
}
}
在超时时间内,数组满了就等待,超过超时间数组还是满的,只能返回false
private void enqueue(E x) {
final Object[] items = this.items;
// 把当前元素插入到数组中去
items[putIndex] = x;
// 放入后存元素的索引等于数组长度(表示已满)
if (++putIndex == items.length)
// 重置存索引为0
putIndex = 0;
// 元素个数加1
count++;
// 唤醒在notEmpty条件上等待的线程
notEmpty.signal();
}
添加成功putIndex加1,加到最大值又变为0
ArrayBlockingQueue 移除
public E poll() {
final ReentrantLock lock = this.lock;
// 获取锁
lock.lock();
try {
// 元素个数不为0,则出队
return (count == 0) ? null : dequeue();
} finally {
// 解锁
lock.unlock();
}
}
元素为0时,返回null
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
// 使用while循环来判断队列是否已空,防止假唤醒(存入成功notEmpty.signal())
while (count == 0)
notEmpty.await();
// 取值
return dequeue();
} finally {
// 解锁
lock.unlock();
}
}
元素为0时notEmpty.await()开始等待,不为空时开始出队
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) {
// 如果等待时间过了队列还是满的话就直接返回false,添加元素失败
if (nanos <= 0)
return null;
// 等待设置的时间
nanos = notEmpty.awaitNanos(nanos);
}
return dequeue();
} finally {
// 解锁
lock.unlock();
}
}
元素个数为0且没有达到超时时间,继续等待,过了超时时间返回null
public boolean remove(Object o) {
if (o == null) return false;
final Object[] items = this.items;
final ReentrantLock lock = this.lock;
// 获取锁
lock.lock();
try {
if (count > 0) {
final int putIndex = this.putIndex;
int i = takeIndex;
do {
if (o.equals(items[i])) {
// 调用移除
removeAt(i);
return true;
}
if (++i == items.length)
i = 0;
} while (i != putIndex);
}
return false;
} finally {
// 解锁
lock.unlock();
}
}
循环遍历,比较两个元素是否一样,一样就删除。(只会删除第一个)
private E dequeue() {
final Object[] items = this.items;
E x = (E) items[takeIndex];
// 将对应的数组下标位置设置为null释放资源
items[takeIndex] = null;
if (++takeIndex == items.length) takeIndex = 0;
count--;
if (itrs != null)
itrs.elementDequeued();
// 激活因为notFull条件而阻塞的线程,比如上面的调用put方法的线程
notFull.signal();
return x;
}
出队成功,清空元素(注意:takeIndex 指针)
void removeAt(final int removeIndex) {
final Object[] items = this.items;
if (removeIndex == takeIndex) {
// 元素置空
items[takeIndex] = null;
if (++takeIndex == items.length)
takeIndex = 0;
count--;
if (itrs != null)
itrs.elementDequeued();
} else {
// 这一段很有意思,实际上就是补坑位
final int putIndex = this.putIndex;
for (int i = removeIndex;;) {
int next = i + 1;
// 环形到头从0再来
if (next == items.length)
next = 0;
// 从零开始找putIndex,把后面的元素都向前移
if (next != putIndex) {
items[i] = items[next];
i = next;
} else {
// 移到原putIndex位置时清空元素,putIndex减1
items[i] = null;
this.putIndex = i;
break;
}
}
count--;
if (itrs != null)
itrs.removedAt(removeIndex);
}
// 放开存入锁
notFull.signal();
}
移除成功notFull.signal(),数组取为空时notEmpty.await()
通过源码可以看到,这里用到两个锁完成,notFull锁和notEmpty锁,存满时notFull.await(),存入成功时notEmpty.signal(),这里需要注意takeIndex、putIndex两个指针,非空非满判断都不用这两个指针,用的是元素个数,所有添加删除方法先判断为空为满,在做入队出队操作。既然能入队,并且putIndex为items.length-1说明前面一定有空值,如果是中间空位只能是remove(Object o)干的,但是remove(Object o)会自动整理空位,所以putIndex为items.length-1时可以放心大胆的把putindex置为0,这样就组成一个环形数组。