ArrayBlockingQueue原理分析(二)-迭代器
概述
在上篇文章的结构图中可以看出,所有的队列最后都实现了Queue接口,而Queue继承了Collection接口,而Collection接口继承了Iterable,由于不同的集合会根据自己集合的特性实现自己的迭代器,那本文就分析一下ArrayBlockingQueue集合迭代器的实现方式,因为之前都是一直使用这玩意,从来不清楚内部如何工作的,所以就拿这个集合的迭代器分析一下。
Iterable接口
public interface Iterable<T> { //顺序迭代器 Iterator<T> iterator(); //遍历集合,里面传入一个Consumer default void forEach(Consumer<? super T> action) { Objects.requireNonNull(action); for (T t : this) { action.accept(t); } } //可分割迭代器 default Spliterator<T> spliterator() { return Spliterators.spliteratorUnknownSize(iterator(), 0); } }
这个方法会返回两个迭代器,后面都会分析,中间那个是遍历集合中的元素,传入一个Consumer,意思是说对集合中的每个元素都执行一下accept方法。
Iterator接口
public interface Iterator<E> { public interface Iterator<E> { //是否有下一个元素 boolean hasNext(); //返回当前的下一个 E next(); //移除,一般调用集合的remove方法进行移除 default void remove() { throw new UnsupportedOperationException("remove"); } //对剩余元素遍历 default void forEachRemaining(Consumer<? super E> action) { Objects.requireNonNull(action); while (hasNext()) action.accept(next()); } }
Spliterator接口
public interface Spliterator<T> { //单个对元素执行给定的动作,如果有剩下元素未处理返回true,否则返回false boolean tryAdvance(Consumer<? super T> action); //对每个剩余元素执行给定的动作,依次处理,直到所有元素已被处理或被异常终止。默认方法调用tryAdvance方法 default void forEachRemaining(Consumer<? super T> action) { do { } while (tryAdvance(action)); } //对任务分割,返回一个新的Spliterator迭代器 Spliterator<T> trySplit(); }
从这三个接口可以看出,第一个接口只是把下面的两个接口封装了一下,其本身基本没有定义迭代相关的方法,就只有一个遍历。Iterator和Spliterator的区别在于一个是顺序遍历,另一个是可分割的遍历,因为Java8为了加快大集合的遍历,采用了分布式的方式,先把大集合拆分成小集合,之后多线程并行遍历。本文不会介绍ArrayBlockingQueue的Spliterator迭代器,只介绍Iterator迭代器。
Itrs类
该类封装了Itr类,而Itr实现了Iterator接口。
class Itrs { /** * Node in a linked list of weak iterator references. * 定义节点,节点中包装了Itr迭代器,当Itr没有强引用存在的时候 * 可以直接回收Itr,然后doSomeSweeping会回收无用的节点 */ private class Node extends WeakReference<Itr> { Node next; Node(Itr iterator, Node next) { super(iterator); this.next = next; } } /** Incremented whenever takeIndex wraps around to 0 */ int cycles = 0; /** Linked list of weak iterator references,首节点 */ private Node head; /** Used to expunge stale iterators ,要被清除的node*/ private Node sweeper = null; private static final int SHORT_SWEEP_PROBES = 4; private static final int LONG_SWEEP_PROBES = 16; //构造方法,将迭代器加入链表 Itrs(Itr initial) { register(initial); } //清除迭代器已经被回收的Node节点 void doSomeSweeping(boolean tryHarder) { // assert lock.getHoldCount() == 1; // assert head != null; int probes = tryHarder ? LONG_SWEEP_PROBES : SHORT_SWEEP_PROBES; Node o, p; final Node sweeper = this.sweeper; boolean passedGo; // to limit search to one full sweep //如果为null,从头开始清理 if (sweeper == null) { o = null; p = head; passedGo = true; } else { o = sweeper; p = o.next; passedGo = false; } for (; probes > 0; probes--) { if (p == null) { if (passedGo) break; o = null; p = head; passedGo = true; } final Itr it = p.get(); final Node next = p.next; if (it == null || it.isDetached()) { // found a discarded/exhausted iterator //这里只要发现了需要清理的node,就重置probes,让循环一直进行下去 probes = LONG_SWEEP_PROBES; // "try harder" // unlink p //移除节点 p.clear(); p.next = null; if (o == null) { head = next; if (next == null) { // We've run out of iterators to track; retire itrs = null; return; } } else o.next = next; } else { o = p; } p = next; } this.sweeper = (p == null) ? null : o; } /** * Adds a new iterator to the linked list of tracked iterators. */ void register(Itr itr) { // assert lock.getHoldCount() == 1; //将加入的节点放在链表的头部 head = new Node(itr, head); } //移除节点 void removedAt(int removedIndex) { for (Node o = null, p = head; p != null;) { final Itr it = p.get(); final Node next = p.next; if (it == null || it.removedAt(removedIndex)) { // unlink p // assert it == null || it.isDetached(); p.clear(); p.next = null; if (o == null) head = next; else o.next = next; } else { o = p; } p = next; } if (head == null) // no more iterators to track itrs = null; } /** * Called whenever the queue becomes empty. * * Notifies all active iterators that the queue is empty, * clears all weak refs, and unlinks the itrs datastructure. */ void queueIsEmpty() { // assert lock.getHoldCount() == 1; for (Node p = head; p != null; p = p.next) { Itr it = p.get(); if (it != null) { p.clear(); it.shutdown(); } } head = null; itrs = null; } /** * Called whenever an element has been dequeued (at takeIndex). */ void elementDequeued() { // assert lock.getHoldCount() == 1; if (count == 0) queueIsEmpty(); else if (takeIndex == 0) takeIndexWrapped(); } }
Itr内部类
从上面的分析可知,Itrs用来管理Itr,将Itr包装到一个Node节点中,然后节点中有一个指针,最终构成一个链表,其中Itr使用了弱引用包装,方便垃圾回收。Itr类是ArrayBlockingQueue内部的一个类,实现了Iterator接口,下面看一下这个类。
属性
private class Itr implements Iterator<E> { /** Index to look for new nextItem; NONE at end,迭代器的迭代位置*/ private int cursor; /** Element to be returned by next call to next(); null if none,下一个元素的值,这个值是队列中的值 */ private E nextItem; /** Index of nextItem; NONE if none, REMOVED if removed elsewhere,下一个元素位置 */ private int nextIndex; /** Last element returned; null if none or not detached. */ private E lastItem; /** Index of lastItem, NONE if none, REMOVED if removed elsewhere */ private int lastRet; }
构造方法
Itr() { // assert lock.getHoldCount() == 0; lastRet = NONE; final ReentrantLock lock = ArrayBlockingQueue.this.lock; lock.lock(); try { //如果队列中没有元素 if (count == 0) { // assert itrs == null; cursor = NONE; nextIndex = NONE; prevTakeIndex = DETACHED; } else { //初始化赋值,takeIndex为消费者消费的位置 final int takeIndex = ArrayBlockingQueue.this.takeIndex; prevTakeIndex = takeIndex; //这里就是下一个待消费数据 nextItem = itemAt(nextIndex = takeIndex); //初始化游标,初始化为takeIndex的下一个位置 cursor = incCursor(takeIndex); if (itrs == null) { //itrs为链表的首节点,如果首节点为null,说明当前队列没有迭代器 itrs = new Itrs(this); } else { //如果有的,就把当前迭代器节点放到链表的最前面 itrs.register(this); // in this order //清除链表中无效的迭代器节点 itrs.doSomeSweeping(false); } prevCycles = itrs.cycles; // assert takeIndex >= 0; // assert prevTakeIndex == takeIndex; // assert nextIndex >= 0; // assert nextItem != null; } } finally { lock.unlock(); } }
从上面可以看出,构造方法,就是为里面的一些属性进行赋值,这些属性都是为了控制迭代过程的。
常用方法
public boolean hasNext() { // assert lock.getHoldCount() == 0; //直接看属性的变量中有没有,有就返回true if (nextItem != null) return true; noNext(); return false; } public E next() { // assert lock.getHoldCount() == 0; //直接从变量中拿,不从队列中获取 final E x = nextItem; if (x == null) throw new NoSuchElementException(); final ReentrantLock lock = ArrayBlockingQueue.this.lock; lock.lock(); try { if (!isDetached()) incorporateDequeues(); // assert nextIndex != NONE; // assert lastItem == null; lastRet = nextIndex; final int cursor = this.cursor; if (cursor >= 0) { //重新更新nextItem,如果先执行next方法,之后执行了两次take方法 //第二次即便take已经把第二个元素取出了,这个时候再调用next,依然为 //执行take方法之前的第二个元素 nextItem = itemAt(nextIndex = cursor); // assert nextItem != null; //更新游标 this.cursor = incCursor(cursor); } else { nextIndex = NONE; nextItem = null; } } finally { lock.unlock(); } return x; }
总结
整个来说,迭代器原理就是使用一些变量控制到迭代的位置,当队列中的元素发生变更的时候,迭代器的控制变量跟着变动就可以,由于ArrayBlockingQueue采用了加锁的方式,所以迭代的过程中队列中删除元素是没有影响的,因为会自动修复迭代器的控制变量,而且只有一个变量修改,所以不会出现线程不安全的问题。一个队列可以有多个迭代器,迭代器被封装在Node中,最后构成一个链表,当队列中的元素更新的时候,方便统一更新迭代器的控制变量。