JDK容器与并发—Queue—ArrayBlockingQueue

来源:互联网 发布:centos的vmdk文件下载 编辑:程序博客网 时间:2024/05/01 00:44

概述

      基于数组的有界阻塞队列。

1)FIFO;

2)ArrayBlockingQueue是经典的有界缓存的实现,其内是固定大小的数组缓存,生产者向其中插入元素,消费者从其取出元素;

3)支持公平锁,默认为非公平锁,公平锁会降低吞吐量但更稳定,避免某个线程阻塞不工作的情况;

数据结构

      Object数组,逻辑上是环形数组


/** The queued items */final Object[] items;/** 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;// 经典的一把锁两个条件的并发控制/** Main lock guarding all access */final ReentrantLock lock;/** Condition for waiting takes */private final Condition notEmpty;/** Condition for waiting puts */private final Condition notFull;

构造器

// 带容量参数构造,默认为非公平锁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);notEmpty = lock.newCondition(); // Queue非空notFull =  lock.newCondition(); // Queue未满}// 带容量、是否为公平锁参数、Collection参数构造// 将Collection中的元素添加进来public ArrayBlockingQueue(int capacity, boolean fair,  Collection<? extends E> c) {this(capacity, fair);final ReentrantLock lock = this.lock;lock.lock(); // 为可见性加锁,非互斥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();}}

增删查

基础方法

// 循环增加final int inc(int i) {return (++i == items.length) ? 0 : i;}// 将元素循环入队private void insert(E x) {items[putIndex] = x;putIndex = inc(putIndex);++count;notEmpty.signal(); // 发送队列非空信号}// 将元素循环出队private E extract() {final Object[] items = this.items;E x = this.<E>cast(items[takeIndex]);items[takeIndex] = null;takeIndex = inc(takeIndex);--count;notFull.signal(); // 发送队列未满信号return x;}

      步骤:

1)竞争获取lock;

2)若队列未满,则才将元素入队,发送队列非空信号;

3)若队列已满,offer立即返回false,add立即抛出IllegalStateException异常,put一直等到队列未满,offer阻塞版则等待timeout时间,超出则立即返回false;

4)释放锁。

// offer的抛出异常版本IllegalStateExceptionpublic boolean add(E e) {return super.add(e);}// 将元素e队列// 非阻塞public boolean offer(E e) {checkNotNull(e);final ReentrantLock lock = this.lock;lock.lock();try {if (count == items.length) // 队列已满,立即返回falsereturn false;else {insert(e);             // 队列未满,将元素入队,返回truereturn true;}} finally {lock.unlock();}}// 将元素e队列// 无限阻塞public void put(E e) throws InterruptedException {checkNotNull(e);final ReentrantLock lock = this.lock;lock.lockInterruptibly();          // 获取到锁前,可中断的锁的获取try {while (count == items.length)  // 队列已满,则一直waitnotFull.await();insert(e);   // 直到等到队列未满时,才将元素入队,发送队列非空信号} finally {lock.unlock();}}// 将元素e队列// 有限阻塞,时长为timeout public boolean offer(E e, long timeout, TimeUnit unit)throws InterruptedException {checkNotNull(e);long nanos = unit.toNanos(timeout);final ReentrantLock lock = this.lock;lock.lockInterruptibly();                  // 获取到锁前,可中断的锁的获取try {while (count == items.length) {        // 队列已满,则等待timeout,超出则立即返回if (nanos <= 0)return false;nanos = notFull.awaitNanos(nanos);}insert(e);   // 等待小于timeout,则将元素入队,发送队列非空信号return true;} finally {lock.unlock();}}

      步骤:
1)竞争获取lock;
2)若队列非空,则将队首元素出队,发送队列未满信号;
3)若队列为空,poll立即返回null,remove立即抛出NoSuchElementException异常,take一直等到队列非空,poll阻塞版则等待timeout时间,超出则立即返回null;
4)释放锁。

// 将队首元素出队// 非阻塞 public E poll() {final ReentrantLock lock = this.lock;lock.lock();try {return (count == 0) ? null : extract(); // 队列为空,返回null;否则将队首元素出队,发送队列未满信号} finally {lock.unlock();}}// 将队首元素出队// 无限阻塞public E take() throws InterruptedException {final ReentrantLock lock = this.lock;lock.lockInterruptibly();             // 获取到锁前,可中断的锁的获取try {while (count == 0)  // 队列为空,则一直waitnotEmpty.await();return extract();  // 直到等到队列非空时,才将队首元素出队,发送队列未满信号} finally {lock.unlock();}}// 将队首元素出队// 有限阻塞 public E poll(long timeout, TimeUnit unit) throws InterruptedException {long nanos = unit.toNanos(timeout);final ReentrantLock lock = this.lock;lock.lockInterruptibly();// 获取到锁前,可中断的锁的获取try {while (count == 0) {// 队列为空,则等待timeout,超出则立即返回nullif (nanos <= 0)return null;nanos = notEmpty.awaitNanos(nanos);}return extract();// 等待小于timeout,则将将队首元素出队,发送队列未满信号} finally {lock.unlock();}}

      步骤:
1)竞争获取lock;
2)队列为空,返回null;否则队首元素;
3)释放锁。

// 获取队首元素public E peek() {final ReentrantLock lock = this.lock;lock.lock();           // 获取locktry {return (count == 0) ? null : itemAt(takeIndex); // 队列为空,返回null;否则队首元素} finally {lock.unlock();     // 释放lock}}

迭代器

      弱一致性。

/** * Iterator for ArrayBlockingQueue. To maintain weak consistency * with respect to puts and takes, we (1) read ahead one slot, so * as to not report hasNext true but then not have an element to * return -- however we later recheck this slot to use the most * current value; (2) ensure that each array slot is traversed at * most once (by tracking "remaining" elements); (3) skip over * null slots, which can occur if takes race ahead of iterators. * However, for circular array-based queues, we cannot rely on any * well established definition of what it means to be weakly * consistent with respect to interior removes since these may * require slot overwrites in the process of sliding elements to * cover gaps. So we settle for resiliency, operating on * established apparent nexts, which may miss some elements that * have moved between calls to next. */private class Itr implements Iterator<E> {private int remaining; // Number of elements yet to be returnedprivate int nextIndex; // Index of element to be returned by nextprivate E nextItem;    // Element to be returned by next call to nextprivate E lastItem;    // Element returned by last call to nextprivate int lastRet;   // Index of last element returned, or -1 if noneItr() {final ReentrantLock lock = ArrayBlockingQueue.this.lock;lock.lock();try {lastRet = -1;if ((remaining = count) > 0)nextItem = itemAt(nextIndex = takeIndex);} finally {lock.unlock();}}public boolean hasNext() {return remaining > 0;}public E next() {final ReentrantLock lock = ArrayBlockingQueue.this.lock;lock.lock();try {if (remaining <= 0)throw new NoSuchElementException();lastRet = nextIndex;E x = itemAt(nextIndex);  // check for fresher valueif (x == null) {x = nextItem;         // we are forced to report old valuelastItem = null;      // but ensure remove fails}elselastItem = x;while (--remaining > 0 && // skip over nulls   (nextItem = itemAt(nextIndex = inc(nextIndex))) == null);return x;} finally {lock.unlock();}}public void remove() {final ReentrantLock lock = ArrayBlockingQueue.this.lock;lock.lock();try {int i = lastRet;if (i == -1)throw new IllegalStateException();lastRet = -1;E x = lastItem;lastItem = null;// only remove if item still at indexif (x != null && x == items[i]) {boolean removingHead = (i == takeIndex);removeAt(i);if (!removingHead)nextIndex = dec(nextIndex);}} finally {lock.unlock();}}}

特性

       数组+有界+阻塞队列。

ArrayBlockingQueue用一把ReentrantLock锁,两个条件notEmpty、notFull,以让增加、删除操作之间互相协作,保证队列中有元素可取、有空间可插入,实现相对于缓存的功能。而在ConcurrentHashMap中增、删、改之间是互斥的,它们的并发意义是不一样的。

疑问

      数组的可见性如何在并发中体现出来?ConcurrentHashMap、ArrayBlockingQueue各不相同。

0 0