源码剖析之ArrayBlockingQueue
程序员文章站
2022-04-21 09:26:37
...
ArrayBlockingQueue 是jdk1.5 新提供的阻塞队列,实现了固定大小的队列。
功能:
1、阻塞的效果 。put时如果元素已经满,那么阻塞,get时 如果队列为空,那么阻塞。
2、是实现生产者消费者模型的极好的备选工具。
实现依赖:
1、lock锁(内存的可见性、互斥访问、限制编译器的代码优化调整)
2、Condition条件通知(线程间的协作)
注意点:代码中多次用的signal 而不是signalAll 有原因的:
1、signalAll 唤醒所有等待的线程,事实上只能有一个通过获得锁,那么会增加锁竞争的几率。效率也低,如果用signal ,那么仅唤醒一个线程,这正是我们所需要的场景!
为了看清楚ArrayBlockingQueue 的工作原理,请参看其源代码的分析如下:
功能:
1、阻塞的效果 。put时如果元素已经满,那么阻塞,get时 如果队列为空,那么阻塞。
2、是实现生产者消费者模型的极好的备选工具。
实现依赖:
1、lock锁(内存的可见性、互斥访问、限制编译器的代码优化调整)
2、Condition条件通知(线程间的协作)
注意点:代码中多次用的signal 而不是signalAll 有原因的:
1、signalAll 唤醒所有等待的线程,事实上只能有一个通过获得锁,那么会增加锁竞争的几率。效率也低,如果用signal ,那么仅唤醒一个线程,这正是我们所需要的场景!
为了看清楚ArrayBlockingQueue 的工作原理,请参看其源代码的分析如下:
package java.util.concurrent; import java.util.concurrent.locks.*; import java.util.*; public class ArrayBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, java.io.Serializable { private static final long serialVersionUID = -817911632652898426L; /** 队列的项,底层是数组,final 修饰意味着 ArrayBlockingQueue 是固定大小的队列 */ private final E[] items; /** 对数组items 下一个 take, poll or remove 操作的索引index */ private int takeIndex; /** 对数组items 下一个 put, offer, or add 操作的索引index */ private int putIndex; /**队列中数据项的数量 ,必须在lock当前锁的情况下才可以使用 注意:count 不需要volitile 修饰, tackIndex putIndex 都一样,因为使用它们的场景都有lock 锁,所以内存可见性和编译代码调整的优化 得到安全的保障!! */ private int count; /* * Concurrency control uses the classic two-condition algorithm * found in any textbook. */ /** 数据访问的锁*/ private final ReentrantLock lock; /** 非空等待条件 */ private final Condition notEmpty; /** 非满等待条件 */ private final Condition notFull; // Internal helper methods /** *数组是循环,如果i == items.length-1 ,那么下一个index =0 从头开始 */ final int inc(int i) { return (++i == items.length)? 0 : i; } /** * Inserts element at current put position, advances, and signals. * Call only when holding lock. 当前的putIndex位置上放置x元素(只能在获取锁的情况下调用) */ private void insert(E x) { items[putIndex] = x; putIndex = inc(putIndex); //插入数据后,下一个要插入的位置需要 +1 ++count; //数量+1 notEmpty.signal(); //含义:一旦插入新数据,那么需要通知notEmpty.wait 条件下等待的线程(take数据的线程) } /** * Extracts element at current take position, advances, and signals. * 取一个元素 (只能在获取锁的情况下调用) */ private E extract() { final E[] items = this.items; E x = items[takeIndex];// 获取当前takeIndex的元素 items[takeIndex] = null; takeIndex = inc(takeIndex); // 调整下一个获取元素的的位置 --count; //数量要减一个 notFull.signal(); //取出后,通知添加数据的线程,items已经不满了。 return x; } /** * 擅长在i位置的item元素(Call only when holding lock.) * */ void removeAt(int i) { final E[] items = this.items; // if removing front item, just advance if (i == takeIndex) { //如果i是下一个将要获取的值,那么这个值直接置空即可!!! items[takeIndex] = null; takeIndex = inc(takeIndex); //增加下一个获取值的位置 } else { // slide over all others up through putIndex. for (;;) { //否则 int nexti = inc(i); if (nexti != putIndex) { //next 不等于 下一个要放数据的位置 items[i] = items[nexti]; //此处的逻辑是移动i+1 之后的元素到i位置上 i = nexti; } else { 如果nexti 为 putIndex items[i] = null; //i = nexti 那么i位置置空,putIndex = i putIndex = i; break; } } } --count; //数量-- notFull.signal(); //含义:删除数据后,要通知notFull条件 已经非空,释放一个notFull.wait 的线程 } /** * */ public ArrayBlockingQueue(int capacity) { this(capacity, false); } public ArrayBlockingQueue(int capacity, boolean fair) { if (capacity <= 0) throw new IllegalArgumentException(); this.items = (E[]) new Object[capacity]; lock = new ReentrantLock(fair); //是否是公平锁 notEmpty = lock.newCondition();//非空的严谨条件 notFull = lock.newCondition(); //非满的条件, //比如:如果put操作因为空间不足,那么将会一直阻塞(notFull wait),如果这期间没有新数据读取了,那么需要调用:notFull wait方法。 } public ArrayBlockingQueue(int capacity, boolean fair, Collection<? extends E> c) { this(capacity, fair); if (capacity < c.size()) throw new IllegalArgumentException(); for (Iterator<? extends E> it = c.iterator(); it.hasNext();) add(it.next()); } /** * 添加元素 */ public boolean add(E e) { return super.add(e); } /** * 添加元素如已满,那么返回false ,否则返回true * * @throws NullPointerException if the specified element is null */ public boolean offer(E e) { if (e == null) throw new NullPointerException(); final ReentrantLock lock = this.lock; lock.lock(); //变更数据结构的操作,必须枷锁,另外count变量也必须在lock加锁的情况下使用 try { if (count == items.length) //如没有空间了,那么返回flase return false; else { insert(e); return true; } } finally { lock.unlock(); } } /** * 在数组的尾部放数据 */ public void put(E e) throws InterruptedException { if (e == null) throw new NullPointerException(); final E[] items = this.items; final ReentrantLock lock = this.lock;//先加锁 lock.lockInterruptibly(); try { try { while (count == items.length) //如果已经满,那么等待 notFull.await(); } catch (InterruptedException ie) { notFull.signal(); // 如果notFull.await 被打断,其实我认为:此处做这个signal 没有太多意义,做了也是白做,因为items.length 、count没有变啊!!! throw ie; } insert(e); } finally { lock.unlock(); } } /** 插入数据 */ public boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException { if (e == null) throw new NullPointerException(); long nanos = unit.toNanos(timeout); final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { for (;;) { if (count != items.length) { //如果count != length 就是数组不满呗!那么直接插入即可 insert(e); return true; } if (nanos <= 0) return false; try { nanos = notFull.awaitNanos(nanos); //等待nanos纳秒 注意必须这样用! } catch (InterruptedException ie) { notFull.signal(); // propagate to non-interrupted thread throw ie; } } } finally { lock.unlock(); } } public E poll() { final ReentrantLock lock = this.lock; lock.lock(); //必须枷锁 try { if (count == 0) //取数据,如果没有那么返回null return null; E x = extract(); return x; } finally { lock.unlock(); } } public E take() throws InterruptedException { final ReentrantLock lock = this.lock; lock.lockInterruptibly(); try { try { while (count == 0) //如果为空,那么notEmpty await 进入等待状态 notEmpty.await(); } catch (InterruptedException ie) { notEmpty.signal(); // propagate to non-interrupted thread throw ie; } E x = extract(); return x; } 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 { for (;;) { if (count != 0) { //如果还有元素,我以及获得lock锁了,所以count 是不会有竞争的 E x = extract(); return x; } if (nanos <= 0) return null; try { nanos = notEmpty.awaitNanos(nanos); //如果没有数据那么 wait nanos的时间 } catch (InterruptedException ie) { notEmpty.signal(); // 传播异常事必要的。但是notEmpty.signal 是完全没必要的 throw ie; } } } finally { lock.unlock(); } } public E peek() { final ReentrantLock lock = this.lock; lock.lock(); try { return (count == 0) ? null : items[takeIndex]; } finally { lock.unlock(); } } // this doc comment is overridden to remove the reference to collections // greater in size than Integer.MAX_VALUE /* */ public int size() { final ReentrantLock lock = this.lock; lock.lock(); try { //返回的count 必须有lock锁 保卫,否则数据事安全的。 return count; } finally { lock.unlock(); } } // 返回还剩下的元素 public int remainingCapacity() { final ReentrantLock lock = this.lock; lock.lock(); try { return items.length - count; } finally { lock.unlock(); } } /** * Removes a single instance of the specified element from this queue, 删除一个在queue中的元素 * */ public boolean remove(Object o) { if (o == null) return false; final E[] items = this.items; final ReentrantLock lock = this.lock; lock.lock(); //删除元素需要加锁 try { int i = takeIndex; //记录的要取数据的位置 int k = 0; //记录已经取得的元素数量 for (;;) { if (k++ >= count)//如果已经遍历的元素数量k 超过count ,那么可以认定已经找完,没有找到 o 数据。 return false; if (o.equals(items[i])) { //如果o == items[i] ,那么删除i位置的元素 removeAt(i); return true; } i = inc(i); //如果没有找到,那么找下一个!! } } finally { lock.unlock(); } } public boolean contains(Object o) { if (o == null) return false; final E[] items = this.items; final ReentrantLock lock = this.lock; //为了防止遍历中,有数据结构的变动 lock.lock(); try { int i = takeIndex; int k = 0; while (k++ < count) { //如果还有元素 if (o.equals(items[i])) //找到一个可以和在以前的人,那么可以直接返回 return true; i = inc(i); } return false; } finally { lock.unlock(); } } public Object[] toArray() { final E[] items = this.items; final ReentrantLock lock = this.lock; lock.lock(); try { Object[] a = new Object[count]; int k = 0; int i = takeIndex; while (k < count) { a[k++] = items[i]; i = inc(i); } return a; } finally { lock.unlock(); } } public <T> T[] toArray(T[] a) { final E[] items = this.items; final ReentrantLock lock = this.lock; //复制需要加锁! lock.lock(); try { if (a.length < count) a = (T[])java.lang.reflect.Array.newInstance( //生成数组实例的方法!! a.getClass().getComponentType(), //数组的 count //数量 ); int k = 0; int i = takeIndex; //取元素的职位,tckeIndex不变的,不能变的!! while (k < count) { //如果k<count 那么说明还有数据!! a[k++] = (T)items[i]; i = inc(i); } if (a.length > count) a[count] = null; //做一个特殊处理,不处理也可以的! return a; } finally { lock.unlock(); } } public String toString() { final ReentrantLock lock = this.lock; lock.lock(); try { return super.toString(); } finally { lock.unlock(); } } /** * 情况缓冲 */ public void clear() { final E[] items = this.items; final ReentrantLock lock = this.lock; lock.lock(); try { int i = takeIndex; //取元素的位置 int k = count; //总元素的数量 while (k-- > 0) { items[i] = null; i = inc(i); //让下一个元素的位置+1 } count = 0; putIndex = 0; takeIndex = 0; notFull.signalAll(); //非满的条件通知 } finally { lock.unlock(); } } /** * 把队列中的item放到c 容器中 */ public int drainTo(Collection<? super E> c) { if (c == null) throw new NullPointerException(); if (c == this) throw new IllegalArgumentException(); final E[] items = this.items; final ReentrantLock lock = this.lock; lock.lock(); //先获得锁 try { int i = takeIndex; int n = 0; int max = count; //指定最大count while (n < max) { c.add(items[i]); items[i] = null; i = inc(i); ++n; } if (n > 0) { //如果n >0 ,其实就是说count >0,那么把相关值重置 count = 0; putIndex = 0; takeIndex = 0; notFull.signalAll(); //此处的signalAll 是必须的,而不能是signal !,他要通知到所有的处于put等待状态的线程!!! } return n; } finally { lock.unlock(); } } /** * 把队列中的item放到c 容器中 */ public int drainTo(Collection<? super E> c, int maxElements) { if (c == null) throw new NullPointerException(); if (c == this) throw new IllegalArgumentException(); if (maxElements <= 0) return 0; final E[] items = this.items; final ReentrantLock lock = this.lock; lock.lock(); try { int i = takeIndex; int n = 0; int sz = count; int max = (maxElements < count)? maxElements : count; while (n < max) { / c.add(items[i]); items[i] = null; i = inc(i); ++n; } if (n > 0) { count -= n; takeIndex = i; notFull.signalAll(); } return n; } finally { lock.unlock(); } } /** * 生成一个迭代器 * * @return an iterator over the elements in this queue in proper sequence */ public Iterator<E> iterator() { final ReentrantLock lock = this.lock; lock.lock(); try { return new Itr(); // 必须枷锁的情况下完成 } finally { lock.unlock(); } } /** * Iterator for ArrayBlockingQueue */ private class Itr implements Iterator<E> { /** * 下一个返回的元素的index值 */ private int nextIndex; /** * 下一个元素 */ private E nextItem; /** * 返回上一次返回的记录的位置,如果上一个操作是删除操作,那么lastRet值为-1 */ private int lastRet; Itr() { lastRet = -1; //初始化为-1 if (count == 0) nextIndex = -1; //如果没有元素,那么nextIndex也置为-1 else { //否则赋相应的值 nextIndex = takeIndex; nextItem = items[takeIndex]; } } public boolean hasNext() { /* * No sync. We can return true by mistake here * only if this iterator passed across threads, * which we don't support anyway. 注意:此方法事不同步的!! 在多线程环境下 可能错误的返回true,nextIndex 只能标志:读取下一个元素从哪儿开始。 jdk不对同步做支持,其事实上即便支持也没什么实际意义!!! */ return nextIndex >= 0; } /** * 如果 nextIndex 是有效的,那么设置nextItem值 */ private void checkNext() { if (nextIndex == putIndex) { //已经put的位置了,已经没有数据可取 nextIndex = -1; nextItem = null; } else { nextItem = items[nextIndex]; if (nextItem == null) nextIndex = -1; } } /* [b]获取下一个元素: 其实此方法我感觉几乎没什么意义! 不准确,还可能抛异常,还不如toArray 得了[/b]*/ public E next() { final ReentrantLock lock = ArrayBlockingQueue.this.lock; lock.lock(); try { if (nextIndex < 0) throw new NoSuchElementException(); lastRet = nextIndex; E x = nextItem; nextIndex = inc(nextIndex); checkNext(); 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; int ti = takeIndex; removeAt(i); // back up cursor (reset to front if was first element) nextIndex = (i == ti) ? takeIndex : i; checkNext(); } finally { lock.unlock(); } } } }