ArrayBolckingQueue
ArrayBlockingQueue是一个用数组实现的有界阻塞队列。此队列按照先进先出(FIFO)的原则对元素进行排序。
ArrayBlockingQueue内部只有一把锁,就是说同一时刻只能有一个线程可以进行put或者take操作。
默认情况下不保证线程公平的访问队列,所谓公平访问队列是指阻塞的线程,可以按照阻塞的先后顺序访问队列,即先阻塞线程先访问队列。非公平性是对先等待的线程是非公平的,当队列可用时,阻塞的线程都可以争夺访问队列的资格,有可能先阻塞的线程最后才访问队列。为了保证公平性,通常会降低吞吐量;
1、继承关系
public class ArrayBlockingQueue<E> extends AbstractQueue<E>
implements BlockingQueue<E>, java.io.Serializable
2、属性
注意:
- items的大小需要在构造ArrayBlockingQueue时进行指定,因为数组前有final修饰,无法扩容,创建之后数组的大小无法再改变;
- 在ArrayBlockingQueue中队列元素个数count为int类型,因为同一时刻只能有一个线程对底层的数组进行put或者take操作,所以不用担心count会被其他线程修改,而在LinkedBlockingQueue中,队列元素的个数count为AtomicInteger类型,在LinkedBlockingQueue中会有提到。
//items是用来存储元素的数组,ArrayBlockingQueue底层是用数组来实现的
final Object[] items;
//读取操作的位置
int takeIndex;
//写操作的位置
int putIndex;
//队列里元素的个数
int count;
//队列同步相关属性,在构造函数中进行初始化
final ReentrantLock lock; //重入锁
//重入锁的两个Condition实例,用于并发
private final Condition notEmpty;
private final Condition notFull;
3、构造函数
public ArrayBlockingQueue(int capacity) {
//通过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();
}
//用集合来初始化ArrayBlockingQueue
public ArrayBlockingQueue(int capacity, boolean fair,Collection<? extends E> c) {
this(capacity, fair);
final ReentrantLock lock = this.lock;
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();
}
}
4、put() 添加元素
在队列已满时,调用put()方法的线程会调用notFull.await
释放锁进入阻塞状态,如果有另一个线程移除了一个数据,就会调用notFull.signal
,这时上一个进行put操作的线程可能被释放从而获取锁,执行完put()方法将元素添加到队列里;
public void put(E e) throws InterruptedException {
checkNotNull(e); //ArrayBlockingQueue不能存储null值
final ReentrantLock lock = this.lock;
lock.lockInterruptibly(); //显性添加可中断锁
try {
while (count == items.length) { //当前容量满了之后处于await状态,在take操作中会给当前发送消息
notFull.await();
}
insert(e);
} finally {
//显性释放锁
lock.unlock();
}
}
=====================================
private void insert(E x) {
items[putIndex] = x; //在数组的putIndex位置插入元素
putIndex = inc(putIndex);
++count; //数组中元素的个数+1,因为
//作用于take操作中,阻塞队列中没有元素时的await状态
notEmpty.signal();
}
5)take() 删除元素
在队列为空时,调用take()方法的线程会调用notEmpty.await
,释放锁进入阻塞状态,若有另一个执行put操作的线程添加了一个元素,就会重新调用notEmpty.signal
,这时上面执行take操作的线程就可能被释放,获取锁之后会将take操作执行完毕;
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock; //获取锁
lock.lockInterruptibly(); //添加可中断锁
try {
while (count == 0) { //若队列为空则阻塞当前线程
notEmpty.await();
}
return extract(); //队列不为空时,返回移除的元素
} finally {
lock.unlock(); //释放锁
}
}
============================
private E extract() {
final Object[] items = this.items;
E x = this.<E>cast(items[takeIndex]); //拿出takeIndex位置的元素
items[takeIndex] = null; //将该位置置为null
takeIndex = inc(takeIndex);
--count;
//作用于put操作中,队列满时的await状态
notFull.signal();
return x; //返回被移除的元素
}