ArrayBlockingQueue是数组实现的线程安全的有界的阻塞队列,可以按照 FIFO(先进先出)原则对元素进行排序。
线程安全是指,ArrayBlockingQueue内部通过“互斥锁”保护竞争资源,实现了多线程对竞争资源的互斥访问。而有界,则是指ArrayBlockingQueue对应的数组是有界限的。 阻塞队列,是指多线程访问竞争资源时,当竞争资源已被某线程获取时,其它要获取该资源的线程需要阻塞等待;所谓公平的访问队列是指阻塞的线程,可以按照阻塞的先后顺序访问队列,先阻塞的线程先访问ArrayBlockingQueue队列。非公平性是对先等待的线程是非公平的,当队列可用时,阻塞的线程都可以争夺访问队列的资格,有可能先阻塞的线程最后才能够访问队列。然而为了保证公平性,通常会降低吞吐量。
1.ArrayBlockingQueue:基于数组实现的一个阻塞队列,在创建ArrayBlockingQueue对象时必须制定容量大小。 并且可以指定公平性与非公平性,默认情况下为非公平的,即不保证等待时间最长的队列最优先能够访问队列。
2.ArrayBlockingQueue内部通过Object[]数组保存数据的,也就是说ArrayBlockingQueue本质上是通过数组实现的。ArrayBlockingQueue的大小,即数组的容量是在创建创建ArrayBlockingQueue时候指定的。
3.如下图所示,ArrayBlockingQueue和ReentrantLock是组合关系,ArrayBlockingQueue中包含一个ReentrantLock对象。ReentrantLock是可重入的互斥锁。ArrayBlockingQueue就是根据ReentrantLock互斥锁实现"多线程对共享资源的访问"。ReentrantLock分为公平锁和非公平锁,关于具体使用公平锁还是非公平锁,在创建ArrayBlockingQueue时可以指定;而且,ArrayBlockingQueue默认会使用非公平锁。
4.ArrayBlockingQueue和Condition是组合关系,ArrayBlockingQueue中包含两个Condition对象(notEmpty和notFull)。使用通知模式实现:所谓通知模式,当生产者往满的队列里面添加元素的时候,会阻塞生产者(调用Condition notFull.await()进行等待);当消费者消费了一个队列中的元素后,会通知(调用Condition notFull.signal()唤醒生产者)生产者当前队列可用。反之,当消费者消费的时候,发现队列是空的,则消费者会被阻塞(通过Condition的 notEmpty.await()进行等待),当生产者插入了队列中的一个元素后,则会调用notEmpty.signal()唤醒消费者继续消费。
关于CONDITION,可以参考《并发编程5-AQS的Condition实现原理》
ArrayBlockingQueue方法列表:
// 创建一个带有给定的(固定)容量和默认访问策略的 ArrayBlockingQueue。
ArrayBlockingQueue(int capacity)
// 创建一个具有给定的(固定)容量和指定访问策略的 ArrayBlockingQueue。
ArrayBlockingQueue(int capacity, boolean fair)
// 创建一个具有给定的(固定)容量和指定访问策略的 ArrayBlockingQueue,它最初包含给定 collection 的元素,并以 collection 迭代器的遍历顺序添加元素。
ArrayBlockingQueue(int capacity, boolean fair, Collection extends E> c)
// 将指定的元素插入到此队列的尾部(如果立即可行且不会超过该队列的容量),在成功时返回 true,如果此队列已满,则抛出 IllegalStateException。
boolean add(E e)
// 自动移除此队列中的所有元素。
void clear()
// 如果此队列包含指定的元素,则返回 true。
boolean contains(Object o)
// 移除此队列中所有可用的元素,并将它们添加到给定 collection 中。
int drainTo(Collection super E> c)
// 最多从此队列中移除给定数量的可用元素,并将这些元素添加到给定 collection 中。
int drainTo(Collection super E> c, int maxElements)
// 返回在此队列中的元素上按适当顺序进行迭代的迭代器。
Iterator iterator()
// 将指定的元素插入到此队列的尾部(如果立即可行且不会超过该队列的容量),在成功时返回 true,如果此队列已满,则返回 false。
boolean offer(E e)
// 将指定的元素插入此队列的尾部,如果该队列已满,则在到达指定的等待时间之前等待可用的空间。
boolean offer(E e, long timeout, TimeUnit unit)
// 获取但不移除此队列的头;如果此队列为空,则返回 null。
E peek()
// 获取并移除此队列的头,如果此队列为空,则返回 null。
E poll()
// 获取并移除此队列的头部,在指定的等待时间前等待可用的元素(如果有必要)。
E poll(long timeout, TimeUnit unit)
// 将指定的元素插入此队列的尾部,如果该队列已满,则等待可用的空间。
void put(E e)
// 返回在无阻塞的理想情况下(不存在内存或资源约束)此队列能接受的其他元素数量。
int remainingCapacity()
// 从此队列中移除指定元素的单个实例(如果存在)。
boolean remove(Object o)
// 返回此队列中元素的数量。
int size()
// 获取并移除此队列的头部,在元素变得可用之前一直等待(如果有必要)。
E take()
// 返回一个按适当顺序包含此队列中所有元素的数组。
Object[] toArray()
// 返回一个按适当顺序包含此队列中所有元素的数组;返回数组的运行时类型是指定数组的运行时类型。
T[] toArray(T[] a)
// 返回此 collection 的字符串表示形式。
String toString()
1.创建:
/**
* Creates an {@code ArrayBlockingQueue} with the given (fixed)
* capacity and default access policy.
*
* @param capacity the capacity of this queue
* @throws IllegalArgumentException if {@code capacity < 1}
只是指定ArrayBlockingQueue的容量,默认采用非公平互斥锁
*/ public ArrayBlockingQueue(int capacity) { this(capacity, false); }
/**
* Creates an {@code ArrayBlockingQueue} with the given (fixed)
* capacity and the specified access policy.
*
* @param capacity the capacity of this queue
* @param fair if {@code true} then queue accesses for threads blocked
* on insertion or removal, are processed in FIFO order;
* if {@code false} the access order is unspecified.
* @throws IllegalArgumentException if {@code capacity < 1}
指定容量和ReetrantLock的类型是否为公平锁创建阻塞队列
*/
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();
}
说明:
items是保存“阻塞队列”数据的数组。它的定义如下:
final Object[] items;
fair是“可重入的独占锁(ReentrantLock)”的类型。fair为true,表示是公平锁;fair为false,表示是非公平锁。notEmpty和notFull是锁的两个Condition条件。它们的定义如下:
final ReentrantLock lock;
private final Condition notEmpty;
private final Condition notFull;
说明:
Lock的作用是提供独占锁机制,来保护竞争的资源;condition提供了类似object.wait和notify的线程通信机制,但是condition支持多个等待队列,使用上更加灵活
notEmpty表示"锁的非空条件"。当某线程想从队列中获取数据的时候,而此时队列中的数据为空,则该线程通过notEmpty.await()方法进行等待;
当其他线程向队列中插入元素之后,就调用notEmpty.signal()方法进行唤醒之前等待的线程。同理,notFull表示“锁满的条件“。当某个线程向队列中插入元素,而此时队列已满时,该线程等待,即阻塞通过notFull.wait()方法;其他线程从队列中取出元素之后,就唤醒该等待的线程,这个线程调用notFull.signal()方法。
2.添加:
/**
* Inserts the specified element at the tail of this queue, waiting
* for space to become available if the queue is full.
*
* @throws InterruptedException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
添加元素,当队列满的时候,该线程等待,即阻塞。
*/
public void put(E e) throws InterruptedException {
//校验插入的元素不能为null
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
//队列满的时候
while (count == items.length)
//线程调用await方法阻塞
notFull.await();
//否则插入数据
insert(e);
} finally {
lock.unlock();
}
}
/** items index for next take, poll, peek or remove
下一个被取出元素的索引
*/
int takeIndex;
/** items index for next put, offer, or add
下一个被添加元素的索引
*/
int putIndex;
/** Number of elements in the queue
队列中的元素的个数
*/
int count;
/**
* Inserts element at current put position, advances, and signals.
* Call only when holding lock.
*/
private void insert(E x) {
items[putIndex] = x;
putIndex = inc(putIndex);
//队列中的元素个数
++count;
//唤醒notEmpty Condition锁上面等待的线程,告诉该线程队列不为空了,可以消费了
notEmpty.signal();
}
/**
* Circularly increment i.
队列中的元素个数==队列的长度的时候,队列满,则设置下一个被添加元素的索引为0
*/
final int inc(int i) {
return (++i == items.length) ? 0 : i;
}
2.取出:
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
//获取锁,如果当前线程是中断状态,则抛出interruptedException异常
lock.lockInterruptibly();
try {
//队列为空的时候,则线程一直阻塞等待
while (count == 0)
notEmpty.await();
//否则取出元素
return extract();
} finally {
lock.unlock();
}
}
/**
* Extracts element at current take position, advances, and signals.
* Call only when holding lock.
*/
private E extract() {
final Object[] items = this.items;
//强制将元素转化为"泛型E"
E x = this.cast(items[takeIndex]);
items[takeIndex] = null;
//设置下一个被取出元素的索引
takeIndex = inc(takeIndex);
//将队列中的元素-1
--count;
//唤醒notFull条件上面等待的线程,告诉该线程队列不是满的了,可以添加元素了
notFull.signal();
return x;
}