是数组实现的线程安全的有界阻塞队列。
内部通过互斥锁保护竞争资源,实现了多线程对竞争资源的互斥访问。
有界,是指ArrayBlockingQueue对应的数组是有界限的。
是按FIFO先进先出原则对元素进行排序,元素都是从尾部插入到队列,从头部开始返回。
-
1.ArrayBlockingQueue继承于AbstractQueue,并且它实现了BlockingQueue接口。
-
ArrayBlockingQueue内部是通过Object[]数组保存数据的,也就是说ArrayBlockingQueue本质上是通过数组实现的。ArrayBlockingQueue的大小,即数组的容量是创建ArrayBlockingQueue时指定的。
-
ArrayBlockingQueue与ReentrantLock是组合关系,ArrayBlockingQueue中包含一个ReentrantLock对象(lock)。ReentrantLock是可重入的互斥锁,ArrayBlockingQueue就是根据该互斥锁实现“多线程对竞争资源的互斥访问”。而且,ReentrantLock分为公平锁和非公平锁,关于具体使用公平锁还是非公平锁,在创建ArrayBlockingQueue时可以指定;而且,ArrayBlockingQueue默认会使用非公平锁。
-
ArrayBlockingQueue与Condition是组合关系,ArrayBlockingQueue中包含两个Condition对象(notEmpty和notFull)。而且,Condition又依赖于ArrayBlockingQueue而存在,通过Condition可以实现对ArrayBlockingQueue的更精确的访问
若某线程(线程A)要取数据时,数组正好为空,则该线程会执行notEmpty.await()进行等待;当其它某个线程(线程B)向数组中插入了数据之后,会调用notEmpty.signal()唤醒“notEmpty上的等待线程”。此时,线程A会被唤醒从而得以继续运行。
若某线程(线程H)要插入数据时,数组已满,则该线程会它执行notFull.await()进行等待;当其它某个线程(线程I)取出数据之后,会调用notFull.signal()唤醒“notFull上的等待线程”。此时,线程H就会被唤醒从而得以继续运行。
创建
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 boolean offer(E e) {
// 创建插入的元素是否为null,是的话抛出NullPointerException异常
checkNotNull(e);
// 获取“该阻塞队列的独占锁”
final ReentrantLock lock = this.lock;
lock.lock();
try {
// 如果队列已满,则返回false。
if (count == items.length)
return false;
else {
// 如果队列未满,则插入e,并返回true。
insert(e);
return true;
}
} finally {
// 释放锁
lock.unlock();
}
}
insert()
public void insert(E x){
items[putIndex] = x;
putIndex = inc(putIndex);
++count;
notEmpty.signal();
}
inc()
final int inc(int i){
return ((++i) == items.length)? 0 : i;
}
若i+1等于队列的长度,队列满,则设置下一个被添加的元素的索引为0
取出
public E take() throws InterruptedException {
// 获取“队列的独占锁”
final ReentrantLock lock = this.lock;
// 获取“锁”,若当前线程是中断状态,则抛出InterruptedException异常
lock.lockInterruptibly();
try {
// 若“队列为空”,则一直等待。
while (count == 0)
notEmpty.await();
// 取出元素
return extract();
} finally {
// 释放“锁”
lock.unlock();
}
}
extract()
private E extract() {
final Object[] items = this.items;
// 强制将元素转换为“泛型E”
E x = this.<E>cast(items[takeIndex]);
// 将第takeIndex元素设为null,即删除。同时,帮助GC回收。
items[takeIndex] = null;
// 设置“下一个被取出元素的索引”
takeIndex = inc(takeIndex);
// 将“队列中元素数量”-1
--count;
// 唤醒notFull上的等待线程。
notFull.signal();
return x;
}
extract()在删除元素之后,会唤醒notFull上的等待线程