ArrayBlockingQueue
ArrayBlockingQueue队列是BlockingQueue的一个子类.block是阻塞的意思。它是一个阻塞队列,array说明它的底层是基于数组实现。数组是有大小的,所以它是一个有(边)界的队列。综上,ArrayBlockingQueue是一个有界的阻塞队列。
BlockingQueue
BlockingQueue接口有三个删除方法和三个添加方法
添加方法
- boolean add(E e); 添加一个元素,成功则返回true,添加失败返回false.如果因为队列容量满了而添加失败抛出IllegalStateException异常
- boolean offer(E e);添加成功返回true,添加失败返回false
- void put(E e) throws InterruptedException; 添加元素,添加成功返回true,如果队列满了就一直阻塞知道队列可以添加元素。可能抛出InterruptedException异常。
删除方法 - E take() throws InterruptedException;删除队列头,并返回其值。如果队列为空,则一直阻塞,知道队列有数据。在阻塞过程中,如果线程中断,那么会抛出InterruptedException异常
- E poll() throws InterruptedException;删除队列头部元素,并返回其值.如果队列为空返回null
- boolean remove(Object o);删除制定的元素对象,删除成功返回true,删除失败返回false.
ArrayBlockingQueue基本原理
/** Main lock guarding all access */
final ReentrantLock lock;
/** Condition for waiting takes */
private final Condition notEmpty;
/** Condition for waiting puts */
private final Condition notFull;
在ArrayBlockingQueue中,有一个独占锁,和这个锁的两个条件对象。一个notEmpy和notFull.通过这个可重入的独占锁,和条件就可以并发控制。
add实现
public boolean add(E e) {
return super.add(e);
}
调用了父类AbstractQueue队列的实现方法,如下:
public boolean add(E e) {
if (offer(e))
return true;
else
throw new IllegalStateException("Queue full");
}
add方法调用的offer()方法。因此看offer源码就行。
offer实现
public boolean offer(E e) {
checkNotNull(e);//检查插入对象是否为空,如果为空抛出空指针异常
final ReentrantLock lock = this.lock;
lock.lock();//上锁
try {
if (count == items.length)//元素的个数等于数组长度,队列已满返回false
return false;
else {
insert(e);//队列没满就直接插入
return true;//插入成功返回true
}
} finally {
lock.unlock();
}
}
空检查
private static void checkNotNull(Object v) {
if (v == null)
throw new NullPointerException();
}
insert方法
private void insert(E x) {
items[putIndex] = x; //插入元素
putIndex = inc(putIndex); //当前插入数据索引加1.如果满了就归0
++count;//元素个数加1
notEmpty.signal();//通知队列为空时候,被阻塞的取线程。
}
put方法实现
public void put(E e) throws InterruptedException {
checkNotNull(e);//空检查,为空抛出空指针异常
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();//获取锁。如果线程被中断,抛出异常
try {
while (count == items.length)//队列已满,阻塞当前线程,加入条件等待队列。
notFull.await();
insert(e);//队列没满插入数据。并通知在等待取数线程。
} finally {
lock.unlock();
}
}
poll方法实现
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();//获取锁
try {
return (count == 0) ? null : extract();//如果队列为空,返回null.否则调用extract()方法 该方法源码看下面。
} finally {
lock.unlock();
}
}
extract方法
private E extract() {
final Object[] items = this.items;
E x = this.<E>cast(items[takeIndex]);//获取取索引上的元素
items[takeIndex] = null;//清空数据
takeIndex = inc(takeIndex);//取数索引加1.如果队列空了就归0
--count;//元素个数减1
notFull.signal();//通知等待添加元素的线程。
return x;//返回元素
}
remove方法
public boolean remove(Object o) {
if (o == null) return false;
final Object[] items = this.items;//
final ReentrantLock lock = this.lock;
lock.lock();//上锁
try {
for (int i = takeIndex, k = count; k > 0; i = inc(i), k--) {
if (o.equals(items[i])) { //从取数索引开始遍历,如果相等调用removeAt方法如果
removeAt(i);
return true;//删除成功就返回true
}
}
return false;//删除失败就返回true
} finally {
lock.unlock();
}
}
removeAt方法
void removeAt(int i) {
final Object[] items = this.items;
// if removing front item, just advance
if (i == takeIndex) { //如果删除的元素是取索引上的值,删除元素,取索引加1
items[takeIndex] = null;
takeIndex = inc(takeIndex);
} else {
// slide over all others up through putIndex.
for (;;) {//如果不是就从删除元素的索引的下一个位置往前移。直到放索引位置,更新放索引退出循环。
int nexti = inc(i);
if (nexti != putIndex) { //如果被删除的元素下一个不是放索引
items[i] = items[nexti]; //将下一个元素前移一个。(循环移动。当前需要删除的元素被它下一个覆盖。所以不需要置空)
i = nexti;
} else {
items[i] = null;//将最后一个元素移除。
putIndex = i;//更新取索引
break;
}
}
}
--count;
notFull.signal();//通知被阻塞的放线程
}
可能上面会有一点点模糊。加入有一个队列【1,2,3,4,5,6,null,null…..】如果移除2.那么在
循环第一次 [1,3,3,4,5,6,null……]
第二次 [1,3,4,4,5,6,null…..]
第三次 [1,3,4,5,5,6,null……]
第四次 [1,3,4,5,6,6,null….]
第五次 [1,3,4,5,6,null,……]
最后一次把最后那个重复的删除,其实就是一个数组移动的过程。
take方法实现
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();//上锁如果线程被中断,抛出异常
try {
while (count == 0) //队列为空,则阻塞当前线程,加入等待队列
notEmpty.await();
return extract(); //调用extract方法。
} finally {
lock.unlock();
}
}