ArrayBlockingQueue
由数组支持的有界阻塞队列。此队列对元素进行 FIFO(先进先出)排序。队列的 头部是在队列中时间最长的元素。队列的尾部是在队列中时间最短的元素。新元素被插入到队列的尾部,队列检索操作获取队列头部的元素。
这是一个经典的“有界缓冲区”,其中一个固定大小的数组保存由生产者插入并由消费者提取的元素。一旦创建,容量将无法更改。尝试将put
一个元素放入一个完整的队列将导致操作阻塞;尝试take
从空队列中获取元素同样会阻塞。
此类支持对等待的生产者和消费者线程进行排序的可选公平策略。默认情况下,不保证此排序。但是,使用公平设置构造的队列以true
FIFO 顺序授予线程访问权限。公平性通常会降低吞吐量,但会降低可变性并避免饥饿。
用法
在实际应用中,设置数组大小时要充分考虑到数据量,如果设置值过小,容易造成堵塞,而且运行中无法修改大小。不支持null元素,否则报NullPointerException异常。
import java.util.concurrent.*;
public class Test {
public static void main(String[] args) {
final BlockingQueue queue = new ArrayBlockingQueue(3);
for(int i=0;i<2;i++){
new Thread(){
public void run(){
while(true){
try {
Thread.sleep((long) (Math.random()*1000));
System.out.println(Thread.currentThread().getName() + "准备放数据!");
queue.put(1);
System.out.println(Thread.currentThread().getName() + "已经放了数据," +
"队列目前有" + queue.size() + "个数据");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
new Thread(){
public void run(){
while(true){
try {
//将此处的睡眠时间分别改为100和1000,观察运行结果
Thread.sleep(100);
System.out.println(Thread.currentThread().getName() + "准备取数据!");
queue.take();
System.out.println(Thread.currentThread().getName() + "已经取走数据," +
"队列目前有" + queue.size() + "个数据");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
}
成员变量
ArrayBlockingQueue是基于数组的队列,锁是基于ReentrantLock。
final Object[] items; //队列数组,循环数组
int takeIndex;//下次出队下标(take、poll、remove),队列满了会重置为0
int putIndex;//下次入队下标(pull、offer、add)
int count;// 队列中元素个数
final ReentrantLock lock;//锁
private final Condition notEmpty;// 当队列为空时,就会调用notEmpty的wait方法,让当前线程等待
private final Condition notFull;//等待条件(入队)
构造函数
- 由于公平锁会降低队列的性能,因而默认使用非公平锁
- 实例化ArrayBlockingQueue必须要设置数组长度,这就是为什么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); //初始化锁对象,fair代表是否公平,公平就是FIFO
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
生产元素
offer
如果可以在不超过队列容量的情况下立即插入指定元素,则在此队列的尾部插入指定元素,true
成功时返回并且false
如果此队列已满。
public boolean offer(E e) {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count == items.length)
return false;//如果队列已满,返回false
else {
enqueue(e);//入队,返回true
return true;
}
} finally {
lock.unlock();
}
}
private void enqueue(E x) {
// assert lock.getHoldCount() == 1;
// assert items[putIndex] == null;
final Object[] items = this.items;向入队下标数组设置元素
items[putIndex] = x;
//如果放入这个元素后队列满了,入队下标设置为0
if (++putIndex == items.length)
putIndex = 0;
count++;
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();一直堵塞,直至被唤醒
enqueue(e);//设置元素
} finally {
lock.unlock();//释放锁
}
}
put和offer方法差别基本不大,只是put方法通过 notFull.await(),一直堵塞,直至被唤醒
消费元素
poll
检索并删除此队列的头部,如果此队列为空,则返回null
。
public E poll() {
final ReentrantLock lock = this.lock;
lock.lock();
try {
return (count == 0) ? null : dequeue();//如果此队列为空,则返回`null`
} finally {
lock.unlock();
}
}
private E dequeue() {
// assert lock.getHoldCount() == 1;
// assert items[takeIndex] != null;
final Object[] items = this.items;
@SuppressWarnings("unchecked")
E x = (E) items[takeIndex];//通过出队下标获取元素
items[takeIndex] = null;
if (++takeIndex == items.length) //消费到队列为空了,重置为0,下次又从头位置消费
takeIndex = 0;
count--;
if (itrs != null)
itrs.elementDequeued();
notFull.signal();//唤醒生产的线程
return x;
}
take
检索并删除此队列的头部,如有必要,等待元素可用。
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0)//如果队列为空,则堵塞,直至被唤醒
notEmpty.await();
return dequeue();//获取元素
} finally {
lock.unlock();
}
}
总结
修饰符和类型 | 方法及说明 |
---|---|
boolean |
add(E e) 如果可以在不超出队列容量的情况下立即插入指定元素,则在此队列的尾部插入指定元素,成功返回并在此队列已满true 时抛出一个 。IllegalStateException |
void |
clear() 原子地从此队列中删除所有元素。 |
boolean |
contains(Object o)如果此队列包含指定的元素,则返回true。 |
Iterator<E> |
iterator() 以正确的顺序返回此队列中元素的迭代器。 |
boolean |
offer(E e) 如果可以在不超过队列容量的情况下立即插入指定元素,则在此队列的尾部插入指定元素,true 成功时返回并且false 如果此队列已满。 |
boolean |
offer(E e, long timeout, TimeUnit unit) 在此队列的尾部插入指定元素,如果队列已满,则等待指定的等待时间以使空间可用。 |
E |
peek() 检索但不删除此队列的头部,null 如果此队列为空,则返回。 |
E |
poll() 检索并删除此队列的头部,null 如果此队列为空,则返回。 |
E |
poll(long timeout, TimeUnit unit) 检索并删除此队列的头部,如果有必要等待指定的等待时间以使元素可用。 |
void |
put(E e) 在此队列的尾部插入指定元素,如果队列已满,则等待空间可用。 |
boolean |
remove(Object o) 从此队列中移除指定元素的单个实例(如果存在)。 |
int |
size() 返回此队列中的元素数。 |
E |
take() 检索并删除此队列的头部,如有必要,等待元素可用。 |
Object[] |
toArray() 按正确的顺序返回包含此队列中所有元素的数组。 |
<T> T[] |
toArray(T[] a) 按正确的顺序返回包含此队列中所有元素的数组;返回数组的运行时类型是指定数组的运行时类型。 |
String |
toString() 返回此集合的字符串表示形式。 |