Java ~ Collection/Executor ~ ArrayBlockingQueue【源码】

前言


    文中的源码注释/结论是我个人学习过程中的理解/看法,多有漏误,后期的新看法/总结也不会再于本文中修正/添加,因此本文内容只可作为参考/提示使用,最新看法/总结以总结篇为准,链接在本文底部。

一 ArrayBlockingQueue(数组阻塞队列)类源码及机制详解


 类

    ArrayBlockingQueue(数组阻塞队列)类(下文简称数组阻塞队列)是BlockingQueue(阻塞队列)接口的主要实现类之一,也是Executor(执行器)框架常用的实现之一,采用数组的方式实现。数组阻塞队列类的底层数组采用了循环的思想,即当元素以插入/放置到数组的尾部后会从数组的头部重新开始插入/放置(如果头部因为移除/拿取而存在空位的话)。数组阻塞队列类是一个标准的FIFO队列,新元素会从尾部插入/放置(即尾插法),而元素的移除/拿取则会在头部进行。

    数组阻塞队列类不允许存null值,或者说BlockingQueue(阻塞队列)接口的所有的实现类都不允许存null值。null被作为poll()及peek()方法作为数组阻塞队列不存在元素的标记值,因此所有的BlockingQueue(阻塞队列)接口实现类都不允许存null值。

    数组阻塞队列类不同于我们常规使用的Collection(集)接口的实现类,没有扩容的说法,即其容量会在创建时确定,之后不会再发生改变。如此一来,其内部的实现结构便相对更加简洁。但这就使得我们必须根据业务的实际情况及资源的硬性限制来选择具体的容量…可一个很现实的问题是早期选定的容量又往往很难支持后期的发展…这也算是个令人很头疼的问题了。

    数组阻塞队列类只能作为有界队列使用,这一点与LinkedBlockingQueue(链接阻塞队列)类不同。数组阻塞队列类只能作为有界队列使用,这意味着在创建其对象时必须指定容量。了解LinkedBlockingQueue(链接阻塞队列)类的同学应该清楚其无界队列用法实质上就是将LinkedBlockingQueue(链接阻塞队列)类对象的容量赋值为Integer.MAX_VALUE。因为这个容量实在太大了,在实际的运行中基本不可能触达上限,因此被称为无界队列。如此一来,只要我们在创建数组阻塞队列时同样将其容量指定为Integer.MAX_VALUE,那其与所谓的“无界队列”便没有什么本质区别。但话虽如此,实际开发中我们是强烈反对这么做的。不仅仅是因为无界队列容易导致OOM(元素的移除/拿取速率低于元素的插入/放置速率,导致元素堆积从而造成OOM),还因为其本身基于数组实现,一个Integer.MAX_VALUE长度的数组也是非常大的对象。因此在实际的开发中,还是推荐指定容量的有界队列用法,即根据业务的实际场景及资源的硬性限制选择合适的容量大小。

    数组阻塞队列类是线程安全的,或者说BlockingQueue(阻塞队列)接口的所有的实现类都是线程安全的(接口的定义中强制要求实现类必须线程安全)。数组阻塞队列类采用“单锁”线程机制来保证线程安全,即使用单个ReentrantLock(可重入锁)类对象来保证整体同步。

    数组阻塞队列类支持公平访问策略,即线程按时间顺序对数组阻塞队列进行访问(插入/放置、移除/拿取等),如果在创建时没有显式指定,则数组阻塞队列默认是非公平的。数组阻塞队列类的公平性选择直接依赖于ReentrantLock(可重入锁)类的公平锁机制实现。

    数组阻塞队列类的迭代器是弱一致性,即可能迭代到已经从移除的元素及无法迭代到新插入的元素。数组阻塞队列类的迭代器实现非常复杂,或者说其实现思想虽然并不复杂,但是在具体实现中有太多的情况需要具体处理,因此整体代码显得非常凌乱无章,难以理解。该知识点的详细内容会在下文详述。

    数组阻塞队列类虽然与BlockingQueue(阻塞队列)接口一样都被纳入Executor(执行器)框架的范畴,但同时也是Collection(集)框架的成员。

/**
 * A bounded {@linkplain BlockingQueue blocking queue} backed by an array.  This queue orders elements FIFO (first-in-first-out).
 * The <em>head</em> of the queue is that element that has been on the queue the longest time.  The <em>tail</em> of the queue is
 * that element that has been on the queue the shortest time. New elements are inserted at the tail of the queue, and the queue retrieval
 * operations obtain elements at the head of the queue.
 * 一个基于数组实现的有界队列。这个队列排序元素先入先出(即标准的队列实现)。队列的头(元素)是在队列中存在时间最长的元
 * 素。队列的尾(元素)是队列中存在时间最短的元素。新元素从队列的尾部插入(尾插法),而检索操作从队列的头部获得元素。
 * <p>
 * This is a classic &quot;bounded buffer&quot;, in which a fixed-sized array holds elements inserted by producers and extracted by
 * consumers.  Once created, the capacity cannot be changed.  Attempts to {@code put} an element into a full queue will result in the
 * operation blocking; attempts to {@code take} an element from an empty queue will similarly block.
 * 这是一个经典的有界缓存,其中固定大小的数组用于持有通过生产者插入,并通过消费者提取的元素。一次创建,容量便不会发生改变。
 * 试图防止一个元素至已经满溢的队列将导致操作阻塞;试图从已经空队列中拿取元素将同样阻塞。
 * <p>
 * This class supports an optional fairness policy for ordering waiting producer and consumer threads.  By default, this ordering is not
 * guaranteed. However, a queue constructed with fairness set to {@code true} grants threads access in FIFO order. Fairness generally
 * decreases throughput but reduces variability and avoids starvation.
 * 这个类支持一个可选的公平策略用于排序等待中的生产者和消费者线程。默认情况是不保证排序的。然而,将公平性设置为true的队列
 * 以FIFO顺序授予线程访问权。公平性通常会降低吞吐量但会降低可变性(我不是很理解可变性的含义,并且没有没什么思考头绪)并避
 * 免饥饿(即线程长时间得不到执行,一般是因为优先级较低)。
 * <p>
 * This class and its iterator implement all of the <em>optional</em> methods of the {@link Collection} and {@link Iterator} interfaces.
 * 类及它的迭代器在Collection接口及Iterator接口中的所有方法都可选的(即不要求实现)。
 * <p>
 * This class is a member of the <a href="{@docRoot}/../technotes/guides/collections/index.html"> Java Collections Framework</a>.
 * 该类是Java集框架的成员
 *
 * @param <E> the type of elements held in this collection 在集中的元素持有的类型
 * @author Doug Lea
 * @Description: 数组阻塞队列类
 * @since 1.5
 */
public class ArrayBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, java.io.Serializable {

    /**
     * Serialization ID. This class relies on default serialization even for the items array, which is default-serialized, even if it is empty.
     * Otherwise it could not be declared final, which is necessary here.
     * 序列化ID。即使项目数组是默认序列化的,该类也依赖于默认序列化,即使它是空的。否则就不能宣布它是最终的,这在这里是
     * 必要的(序列化...不是很懂)。
     */
    private static final long serialVersionUID = -817911632652898426L;
    
    ...
    
}

 字段

    items(元素数组) —— 用于持有保存元素的数组对象的引用。元素数组会在创建队列时根据传入的容量实例化,因此数组的长度就是队列的容量,也使得ArrayBlockingQueue(数组阻塞队列)类没有专门的字段用于记录容量。

/**
 * The queued items
 * 队列项目列表
 *
 * @Description: 元素数组:用于保存元素的数组
 */
final Object[] items;

    takeIndex(拿取索引) —— 用于记录下个拿取元素的索引。移除/拿取操作会通过该字段找到需要移除/拿取的元素并执行操作,随后会将拿取索引递增,以定位下个移除/拿取的元素。

/**
 * items index for next take, poll, peek or remove
 * 下个移除/拿取(remove(),poll(),take()或peek())的项目索引
 *
 * @Description: 拿取索引:用于记录下个拿取元素的索引。
 */
int takeIndex;

    putIndex(放置索引) —— 用于记录下个放置元素的索引。插入/放置操作会通过该字段定位元素数组的插入位置并将元素插入,随后会将放置索引递增,为下次插入/放置提供定位。

/**
 * items index for next put, offer, or add
 * 下个插入/放置(add(),offer()或put())的项目索引
 *
 * @Description: 放置索引:用于记录下个放置元素的索引
 */
int putIndex;

    count(总数) —— 用于记录队列中元素的总数。

/**
 * Number of elements in the queue
 * 队列的元素数量
 *
 * @Description: 总数:用于记录队列中元素的总数
 */
int count;

    lock(锁) —— 用于持有一个ReentrantLock(可重入锁)类对象(下文简称拿取锁)的引用,来保证队列运行时的线程安全。

/**
 * Main lock guarding all access
 * 主锁保卫所有访问
 *
 * @Description: 锁:用于持有一个ReentrantLock(可重入锁)类对象,用于队列在并发环境中进行同步
 */
final ReentrantLock lock;

    notEmpty(非空) —— 用于持有一个Condition(条件)类对象(下文简称非空/拿取者条件)。非空/拿取者条件通过锁创建得来,用于管理因为队列中不存在元素而无法执行移除/拿取操作的线程(下文简称拿取者)。

/**
 * Wait queue for waiting takes
 * 等待拿取的等待/条件队列
 */
private final Condition notEmpty = takeLock.newCondition();

    notFull(非满) —— 用于持有一个Condition(条件)类对象(下文简称非满/放置者条件)。非满/放置者条件通过锁创建得来,用于管理因为队列容量已满而无法执行插入/放置操作的线程(下文简称放置者)。

/**
 * Condition for waiting puts
 * 等待放置的条件
 *
 * @Description: 非满:用于持有一个Condition(条件)类对象(下文简称非满/放置者条件)。
 */
private final Condition notFull;

    itrs(迭代器链) —— 用于持有一个迭代器链对象的引用。迭代器链对象中保存着所有未被切换为分离模式的迭代器,作为队列与迭代器之间沟通的媒介。当队列发生变化时,会通过该对象遍历所有的未分离迭代器对之进行调整。

/**
 * Shared state for currently active iterators, or null if there are known not to be any.  Allows queue operations to update iterator
 * state.
 * 分享当前地活动迭代器状态,或如果一直没有任何(迭代器)则为null。允许队列操作更新迭代器状态。
 *
 * @Description: 迭代器链:用于持有一个迭代器链对象的引用。迭代器链对象中保存着所有未被切换为分离模式的迭代器,作为队
 * @Description: 列与迭代器之间沟通的媒介。当队列发生变化时,会通过该对象遍历所有的未分离迭代器对之进行调整。
 */
transient Itrs itrs = null;

 构造方法

    public ArrayBlockingQueue(int capacity) —— 用于创建一个指定容量的非公平ArrayBlockingQueue(数组阻塞队列)类对象,该构造方法是现实开发中最常用的,需根据业务的实际场景及资源的硬性限制选择合适的容量大小。

/**
 * 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}
 *                                  非法参数异常:如果容量小于1
 */
public ArrayBlockingQueue(int capacity) {
    // 默认是非公平访问。
    this(capacity, false);
}

    public ArrayBlockingQueue(int capacity, boolean fair) —— 用于创建一个指定容量及访问策略的ArrayBlockingQueue(数组阻塞队列)类对象。该方法的特点在于其可以通过fair(公平)参数指定线程是否公平的对队列进行访问(即按时间顺序),而通过源码可以该功能是直接通过ReentrantLock(可重入锁)类的公平锁机制完成的。

/**
 * 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.
 *                 如果为true则队列线程访问会在插入及移除中阻塞,并按FIFO的顺序执行;如果为false则不指定访问顺序。
 * @throws IllegalArgumentException if {@code capacity < 1}
 *                                  非法参数异常:如果容量小于1
 */
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();
}

    public ArrayBlockingQueue(int capacity, boolean fair, Collection<? extends E> c) —— 用于创建一个指定容量及访问策略,且含有初始元素的ArrayBlockingQueue(数组阻塞队列)类对象。该方法的特点除了可以指定访问策略以外,还能够将指定集内包含的元素加入到队列中,从而得到一个包含初始元素的ArrayBlockingQueue(数组阻塞队列)类对象。

/**
 * Creates an {@code ArrayBlockingQueue} with the given (fixed) capacity, the specified access policy and initially containing the elements
 * of the given collection, added in traversal order of the collection's iterator.
 * 创建一个指定容量,指定访问策略及最初包含指定集元素的数据阻塞队列,按集的迭代器遍历顺序加入(队列)。
 *
 * @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.
 *                 如果为true则队列线程访问会在插入及移除中阻塞,并按FIFO的顺序执行;如果为false则不指定访问顺序。
 * @param c        the collection of elements to initially contain 集的元素用于最初包含
 * @throws IllegalArgumentException if {@code capacity} is less than {@code c.size()}, or less than 1.
 *                                  非法参数异常:如果容量小于集的大小或者小于1
 * @throws NullPointerException     if the specified collection or any of its elements are null
 *                                  空指针异常:如果指定集或集中的任意元素为null(阻塞队列的实现类都是不允许存null值的)
 */
public ArrayBlockingQueue(int capacity, boolean fair, Collection<? extends E> c) {
    // 设置容量与访问策略。
    this(capacity, fair);
    // 在锁的保护下,将指定集中的元素加入到队列的元素数组中。
    final ReentrantLock lock = this.lock;
    lock.lock();
    // Lock only for visibility, not mutual exclusion
    // 只是为了可见性而加锁,而不是为了互斥。
    try {
        int i = 0;
        try {
            for (E e : c) {
                checkNotNull(e);
                items[i++] = e;
            }
        } catch (ArrayIndexOutOfBoundsException ex) {
            //     如果在迁移的过程中,发现容量的大小不足以容纳指定集的全部元素,则会抛出非法参数异常。但比较令人惊讶的是作者竟然会使
            // 用catch块的方式进行处理,而不是先进行比对。
            throw new IllegalArgumentException();
        }
        // 设置总数与放置索引。
        count = i;
        putIndex = (i == capacity) ? 0 : i;
    } finally {
        lock.unlock();
    }
}

    我们可以在上述的源码中发现将指定集中的元素存入队列的整个过程是加了锁的,而源码给出的英文注释是“只是为了可见性而加锁,而不是为了互斥”。其中本人对可见性是可以理解的(如果理解没有错的话):如果在过程中没有加锁,就会出现获取不到元素或元素不完整的情况。我们可以很轻松的理解获取不到元素的情况,但为什么会出现元素不完整的问题呢?这是因为在Java中对象的创建不是原子操作,整个过程按顺序大致可分为以下三部分:

  • 实例化(分配内存);
  • 初始化(执行构造方法);
  • 引用赋值(令变量持有对象的引用)。

    但现实中由于指令重排序的原因,具体在执行中顺序就可能变成:

  • 实例化(分配内存);
  • 引用赋值(令变量持有对象的引用);
  • 初始化(执行构造方法)。

    这就意味着加入到队列(存入元素数组)的元素可能是个不完整的元素,因此整个过程必须在锁的保护下执行。锁的存在虽然无法避免同步块中的指令重排序,但可以避免其重排序到同步块外(这就使得元素的初始化一定会在同步块中完成),因此使得队列创建完成后进行获取的线程获取到的元素一定是完整的。

    如果上述我的理解都是对的,那就有一个讲不通的地方就是既然对象的创建不是原子操作,那完全可能存在有线程向一个未执行初始化(或未执行完初始化)的队列保存元素的可能,如此应该是可能存在竞争的,而不是英文注释中说的“没有竞争”,这也是我目前尚未完全理解的地方…当然,也可能是我对可见性的理解本就是错的,希望有懂的同学能在评论区不吝赐教,本人万分感谢。

 方法

    public boolean add(E e) —— 新增 —— 向队列尾部插入/放置一个元素,当队列存在空闲容量时插入成功返回true;否则抛出非法状态异常。该方法是插入/放置操作“异常”形式的实现。当队列容量已满而无法容纳插入/放置的元素时会抛出非法状态异常。在ArrayBlockingQueue(数组阻塞队列)类中我们可以发现实际上其并没有实现该方法,该方式是直接通过调用父类AbstractQueue(抽象队列)抽象类的同名方法实现的,因此本质上可以无需重写该方法,此处重写的目的大概率是为了添加注释。而在AbstractQueue(抽象队列)抽象类中add(E e)方法是直接通过调用offer(E e)方法实现,因此ArrayBlockingQueue(数组阻塞队列)类只需实现offer(E e)方法即可,这种代码格式属于模板模式(设计模式的一种)的范畴。

/**
 * Inserts the specified element at the tail of this queue if it is possible to do so immediately without exceeding the queue's capacity, returning
 * {@code true} upon success and throwing an {@code IllegalStateException} if this queue is full.
 * 如果没有超过超过队列的容量则直接在队列的尾部插入指定元素,成功后返回true。如果队列已满则抛出非法状态异常。
 *
 * @param e the element to add 新增的元素
 * @return {@code true} (as specified by {@link Collection#add}) 按Collection.add()方法的说明返回true
 * @throws IllegalStateException if this queue is full
 *                               非法状态异常:如果队列已满
 * @throws NullPointerException  if the specified element is null
 *                               空指针异常:如果指定元素为null
 * @Description: 新增:用于向队列插入/放置一个元素,当队列存在空闲容量时插入成功返回true;否则抛出非法状态异常。该方法是插入/放置
 * @Description: 操作“特殊值”形式的实现。
 */
@Override
public boolean add(E e) {
    //     该方法直接调用父类(即AbstractQueue抽象类)的同名方法实现,而其的则是通过模板模式调用offer()方法实现的...所以理论上应该可
    // 以不用重写该方法。
    return super.add(e);
}

    public boolean offer(E e) —— 提供 —— 向队列尾部插入/放置一个元素,当队列存在空闲容量时插入成功返回true;否则返回false。该方法是插入/放置操作“特殊值”形式的定义。当使用有界队列时更适合使用此方法,因为不会因为频繁的触及容量上限而不断抛出异常。

    元素的入队是通过enqueue(E x)方法实现的,该方法会在下文详述。

/**
 * Inserts the specified element at the tail of this queue if it is possible to do so immediately without exceeding the queue's capacity, returning
 * {@code true} upon success and {@code false} if this queue is full.  This method is generally preferable to method {@link #add}, which can fail to
 * insert an element only by throwing an exception.
 * 如果没有超过超过队列的容量则直接在队列的尾部插入指定元素,成功后返回true。如果队列已满则返回false。该方法通常比add()方法更好,
 * 其(add()方法)只会在插入元素失败时抛出一个异常。
 *
 * @throws NullPointerException if the specified element is null
 *                              空指针异常:如果指定元素为null
 * @Description: 提供:向队列插入/放置一个元素,当队列存在空闲容量时插入成功返回true;否则返回false。
 */
@Override
public boolean offer(E e) {
    // 检查元素是否为空。
    checkNotNull(e);
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        if (count == items.length)
            // 如果元素数组已满,则直接返回false。
            return false;
        else {
            // 如果元素数组未满,则进行入队,并在成功后返回true。
            enqueue(e);
            return true;
        }
    } finally {
        lock.unlock();
    }
}

    private static void checkNotNull(Object v) —— 检查非空 —— 用于检查传入的元素是否为空,是则抛出空指针异常。

/**
 * Throws NullPointerException if argument is null.
 * 如果参数为null则抛出空指针异常。
 *
 * @param v the element 元素
 * @Description: 检查非空:用于检查传入的元素是否为空,是则抛出空指针异常。
 */
private static void checkNotNull(Object v) {
    if (v == null)
        throw new NullPointerException();
}

    private void enqueue(E x) —— 入队 —— 用于将元素存放于元素数组中的放置索引位置,并循环递增放置索引(即达到元素数组末尾后从头开始)。除了保存索引以外,该方法还负责唤醒可能存在的挂起的拿取者,即对其发送信号。

/**
 * Inserts element at current put position, advances, and signals. Call only when holding lock.
 * 在当前放置位置插入元素,(插入后)前进(放置索引)并(向挂起的拿取者发送)信号(这注释...)。该方法只能在持有锁时调用。
 *
 * @Description: 入队:用于将元素存放于元素数组中的放置索引位置,并循环递增放置索引(即达到元素数组末尾后从头开始)
 */
private void enqueue(E x) {
    // assert lock.getHoldCount() == 1;
    // assert items[putIndex] == null;
    // 断言锁的持有总数为1(即有线程持有锁);
    // 断言元素数组的放置索引位置为null。

    // 在当前放置索引的位置存放元素。
    final Object[] items = this.items;
    items[putIndex] = x;
    //     如果下个放置索引的位置超出了元素数组的范围(即容量),则将之设置为0,意喻着从头部开始保存。该操作表示数组的本质
    // 是一个环。
    if (++putIndex == items.length)
        putIndex = 0;
    // 递增总数,并向挂起的拿取者发送信号...如果存在的话。
    count++;
    notEmpty.signal();
}

    public void put(E e) —— 放置 —— 向队列尾部插入/放置一个元素,当队列已满时会阻塞直至空出空闲容量。由于理论上一定会成功,因此该方法没有返回值。该方法是插入/放置操作“阻塞”形式的定义。具体的做法是在锁的保护下判断当前队列的容量是否已满,是则将当前放置者挂起(通过调用非满/放置者条件的await()方法实现),直至因为中断/信号等原因被唤醒;否则直接执行插入/放置操作。关于对队列容量的判断需要在循环中执行,因为即使放置者被唤醒并再次获取了锁,也不能保证当前队列存在剩余容量。这是由于可能已经有其它放置者先一步获得放置锁并完成了插入/放置,导致队列容量再次满溢(这种情况下获得先一步获得放置锁的放置者会是外来的),因此必须在放置锁的保护下重复的判断。

/**
 * 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} 空指针异常
 * @Description: 放置:用于向队列插入/放置一个元素,当队列已满时会阻塞至空出空闲容量。由于理论上一定会成功,因此该方法没有返回值。
 */
@Override
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();
    }
}

    public boolean offer(E e, long timeout, TimeUnit unit) —— 提供 —— 向队列尾部插入/放置一个元素,当队列已满时会阻塞。如果在指定的时间内空出空闲容量并插入成功则返回true;否则返回false。该方法是插入/放置操作“超时”形式的实现。offer(E e, long timeout, TimeUnit unit)方法的逻辑与put(E e)方法基本相似,差别在于其只会限时挂起(通过调用非满/放置者条件的awaitNanos()方法实现),并且当超出指定的等待时间后会直接返回false。

/**
 * Inserts the specified element at the tail of this queue, waiting up to the specified wait time for space to become available if the queue is full.
 * 向队列的尾部插入指定元素,如果队列已满则等待指定的等待时间至空间变得可用
 *
 * @throws InterruptedException {@inheritDoc} 中断异常
 * @throws NullPointerException {@inheritDoc} 空指针异常
 * @Description: 提供:向队列插入/放置一个元素,当队列已满时会阻塞。如果在指定的时间内空出空闲容量插入成功则返回true;否则返回false。
 */
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 {
        // 循环判断容量是否已满,如果已满则限时挂起,超出时间后直接返回false。
        while (count == items.length) {
            if (nanos <= 0)
                return false;
            nanos = notFull.awaitNanos(nanos);
        }
        // 如果在指定的等待时间内空出剩余空间,则执行入队。
        enqueue(e);
        return true;
    } finally {
        lock.unlock();
    }
}

    public E poll() —— 轮询 —— 从队列头部移除/拿取一个元素,当队列存在元素时将头部元素移除并返回;否则返回null。该方法是移除/拿取操作“特殊值”形式的实现。正是因为null被作为了队列为空的标记值,所以ArrayBlockingQueue(数组阻塞队列)类才不允许存null值。

    元素的出队通过dequeue()方法完成,该方法会在下文详述。

/**
 * @Description: 轮询:从队列移除/拿取一个元素,移除成功返回元素,队列为空则返回null。
 */
public E poll() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        return (count == 0) ? null : dequeue();
    } finally {
        lock.unlock();
    }
}

    private E dequeue() —— 出队 —— 从元素数组中的拿取索引位置获取元素,并循环递增拿取索引(即达到元素数组末尾后从头开始)。除上述功能以外,dequeue()方法还负责对可能存在的挂起的放置者进行唤醒。

    由于元素的出队可能会对迭代器产生影响,因此dequeue()方法还通过迭代器链的elementDequeued()方法来对其中保存的迭代器进行调整。该方法大致有两个作用:一是如果队列为空则将所有的迭代器分离(分离是迭代器的一种状态,意味着迭代器不会再因为队列的变化而发生变化。分离并不意味着迭代器失效,但一个分离的迭代器即使不失效也基本处于濒临失效的状态);而是在队列还存在元素的情况下将已超过一次循环(即拿取索引的重置次数)的迭代器分离。这些迭代器未必不能继续迭代,因为ArrayBlockingQueue(数组阻塞队列)类实现的迭代器是弱一致性的,因此即使返回已被移除/拿取的元素也时允许的。但即便如此,ArrayBlockingQueue(数组阻塞队列)类也不希望将太久未执行迭代的迭代器继续留存,因为这样的会显得数据的一致性过于“弱”。elementDequeued()方法会在下文介绍迭代器链中详述。

/**
 * Extracts element at current take position, advances, and signals. Call only when holding lock.
 * 在当前拿取位置提取元素,(拿取后)前进(拿取索引)并(向挂起的放置者发送)信号。该方法只能在持有锁时调用。
 *
 * @Description: 出队:从元素数组中的拿取索引位置获取元素,并循环递增拿取索引(即达到元素数组末尾后从头开始)
 */
private E dequeue() {
    // assert lock.getHoldCount() == 1;
    // assert items[takeIndex] != null;
    // 断言锁的持有总数为1(即有线程持有锁);
    // 断言元素数组的拿取索引位置不为null。

    // 从拿取索引位置获取元素。
    final Object[] items = this.items;
    @SuppressWarnings("unchecked")
    E x = (E) items[takeIndex];
    items[takeIndex] = null;
    //     如果下个拿取索引的位置超出了元素数组的范围(即容量),则将之设置为0,意喻着从头部开始拿取。该操作表示数组的本质
    // 是一个环。
    if (++takeIndex == items.length)
        takeIndex = 0;
    // 递减总数,并通知迭代器,并对可能挂起的放置者发送信号...如果存在的话。
    count--;
    if (itrs != null)
        itrs.elementDequeued();
    notFull.signal();
    return x;
}

    public E take() —— 拿取 —— 从队列头部移除/拿取一个元素,当队列为空时会阻塞直至移除/拿取成功并返回元素。该方法是移除/拿取操作“阻塞”形式的实现。

/**
 * @Description: 拿取:从队列移除/拿取一个元素,队列为空时会阻塞直至移除成功返回元素。
 */
@Override
public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        // 循环判断队列是否为空。
        while (count == 0)
            notEmpty.await();
        // 出队并返回元素。
        return dequeue();
    } finally {
        lock.unlock();
    }
}

    public E poll(long timeout, TimeUnit unit) —— 轮询 —— 从队列头部移除/拿取一个元素,当队列为空时会阻塞指定的等待时间,如果队列在指定时间内移除/拿取成功则返回元素;否则返回null。该方法是移除/拿取操作“超时”形式的实现。

/**
 * @Description: 轮询:从队列移除/拿取一个元素,队列为空时会阻塞指定的等待时间,如果队列在指定时间内有元素加入则执行移除并返回
 * @Description: 元素;否则返回null。
 */
@Override
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) {
            if (nanos <= 0)
                return null;
            // 限时挂起。
            nanos = notEmpty.awaitNanos(nanos);
        }
        // 出队。
        return dequeue();
    } finally {
        lock.unlock();
    }
}

    public E peek() —— 窥视 —— 从队列头部获取一个元素,当队列存在元素时将头部元素返回;否则返回null。该方法是检查方法的“特殊值”形式的实现。正是因为null被作为了队列为空的标记值,所以ArrayBlockingQueue(数组阻塞队列)类才不允许存null值。

/**
 * @Description: 窥伺:获取但不移除队列的头元素,当队列为空时返回null。
 */
public E peek() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        return itemAt(takeIndex); // null when queue is empty
    } finally {
        lock.unlock();
    }
}

    public int size() —— 大小 —— 用于返回队列已保存的元素数量。

/**
 * Returns the number of elements in this queue.
 * 返回队列中元素的数量
 *
 * @return the number of elements in this queue 队列中元素的数量
 * @Description: 大小:用于获取队列中元素的数量。
 */
public int size() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        return count;
    } finally {
        lock.unlock();
    }
}

    public int remainingCapacity() —— 剩余容量 —— 获取队列的空闲容量。该方法由于并发的原因,并不推荐将该方法的返回值作为是否插入的判断依据(至少不能作为精确的判断依据),因为在获取后及插入/放置前可能有其它线程执行了插入/放置操作导致容量耗尽。

/**
 * Returns the number of additional elements that this queue can ideally (in the absence of memory or resource constraints) accept without
 * blocking. This is always equal to the initial capacity of this queue less the current {@code size} of this queue.
 * 返回该队列理论上能接受而不阻塞的新增元素数量(在没有内存与资源限制的情况下)。这通常等于队列的初始容量减去队列的当前大小。
 * <p>
 * Note that you <em>cannot</em> always tell if an attempt to insert an element will succeed by inspecting {@code remainingCapacity} because
 * it may be the case that another thread is about to insert or remove an element.
 * 注意:你不能经常地通过检查剩余容量试图成功新增一个元素,因它可能是被其它线程新增或移除一个元素(而使其无效)的情况。
 *
 * @Description: 剩余容量:获取队列的剩余容量。
 */
@Override
public int remainingCapacity() {
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        return items.length - count;
    } finally {
        lock.unlock();
    }
}

    public boolean remove(Object o) —— 移除 —— 从队列中移除指定元素的一个实例,移除成功则返回true;否则返回false(由于一般只要存在就可以移除成功,所以本质应该是存在就返回true;否则返回false)。该方法可能从队列的任意位置移除,因此会导致数据迁移的发生,并不推荐使用。

    元素的移除通过removeAt()方法来完成,该方法会在下文详述。

/**
 * Removes a single instance of the specified element from this queue, if it is present.  More formally, removes an element {@code e} such that
 * {@code o.equals(e)}, if this queue contains one or more such elements. Returns {@code true} if this queue contained the specified element
 * (or equivalently, if this queue changed as a result of the call).
 * 从队列中移除指定元素的单个实例(如果它存在)。更正式地,如果队列存在一个或多个如此元素,应该使用o.equals(e)的方式(判断存在
 * 并)移除。如果队列包含指定的元素(或等效地,如果调用的结果是队列发生改变)则返回true。
 * <p>
 * Removal of interior elements in circular array based queues is an intrinsically slow and disruptive operation, so should be undertaken only
 * in exceptional circumstances, ideally only when the queue is known not to be accessible by other threads.
 * 从基于环形数组实现的队列中移除一个内部元素(即不从数据段的两边移除)本质上是一个缓慢且破坏性的操作,所以应该只在特殊的情况
 * (理想地只在队列已知无法被其它线程访问的时候)执行(就是通常情况下不要执行这个方法,原因是因为从数组段的内部移除元素会导致
 * 后续数据向"前"迁移,耗时耗力,并且查找也是一个麻烦事)。
 *
 * @param o element to be removed from this queue, if present 从队列中移除的元素(如果存在)
 * @return {@code true} if this queue changed as a result of the call 如果调用的结果是队列发生改变则返回true
 * @Description: 移除:从队列中移除指定元素的一个实例,移除成功则返回true;否则返回false(由于一般只要存在就可以移除成功,所以本
 * @Description: 质应该是存在就返回true;否则返回false)。该方法可能从队列的任意位置移除,可能导致数据迁移的发生,应该并不推荐使
 * @Description: 用。
 */
@Override
public boolean remove(Object o) {
    // 所有阻塞队列的实现类都是不允许保存null的,因此如果指定元素为null可以直接返回false。
    if (o == null) return false;
    // 在加锁的情况下移除元素。
    final Object[] items = this.items;
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        if (count > 0) {
            // 以拿取索引为起点,放置索引为终点进行遍历。找到第一个指定的元素,并将之移除。
            final int putIndex = this.putIndex;
            int i = takeIndex;
            do {
                // 如果相等则移除(可能是两边移除,也可能是中间移除),并返回true。
                if (o.equals(items[i])) {
                    removeAt(i);
                    return true;
                }
                if (++i == items.length)
                    i = 0;
            } while (i != putIndex);
        }
        return false;
    } finally {
        lock.unlock();
    }
}

    void removeAt(final int removeIndex) —— 移除所在 —— 从元素数组的指定位置移除元素,如果是从中间移除,则需要进行数据的迁移。该方法中涉及的移除包含两种:一是移除/拿取;二是中途移除。对于移除/拿取,逻辑基本与dequeue()方法完全相似,因此不再进行详述;而对于中途移除,则是相对较为复杂的操作。单纯从元素数组的角度上来说,了解数组的同学一定清楚,为了保证数据的连续性,一旦从数组的中途移除元素,就必须迁移后续元素,否则就会造成数据的中断。ArrayBlockingQueue(数组阻塞队列)类同样也是如此,因此是实际开发中我们是强烈不推荐开发者调用该方法的。

    实际上,迁移后续元素仅仅是最简单的附加操作,如果我们把影响的范围扩大到迭代器上,就会知道其会造成多么复杂的影响。迭代器链的removedAt(int removedIndex)方法是当元素数据发生中途移除时调用的方法,其作用遍历所有的迭代器,针对移除的元素对迭代器进行调整。调整的难度并不高,但是非常繁琐,许多具体的情况需要的具体操作,这就是的迭代器的同步变得相当复杂晦涩。

/**
 * Deletes item at array index removeIndex. Utility for remove(Object) and iterator.remove. Call only when holding lock.
 * 删除数组中移除索引位置的条目(元素)。实用于remove(Object o)方法以及迭代器的remove()方法。只能在持有锁时调用。
 *
 * @Description: 移除所在:从元素数组的指定位置移除元素,如果是从中间移除,则需要进行数据的迁移
 */
void removeAt(final int removeIndex) {
    // assert lock.getHoldCount() == 1;
    // assert items[removeIndex] != null;
    // assert removeIndex >= 0 && removeIndex < items.length;
    // 断言锁的持有总数为1(即有线程持有锁);
    // 断言元素数组的移除索引位置不为null;
    // 断言移除索引>= 0且<=元素数组的长度(即容量)。

    final Object[] items = this.items;
    if (removeIndex == takeIndex) {
        //     如果移除索引等于拿取索引,意味着此次移除等价于正常的元素移除/拿取,只需将元素数组的拿取索引位置置null并前进拿取
        // 索引即可..当然,还有操作迭代器。

        // removing front item; just advance
        // 移除前一个条目;只需前进
        items[takeIndex] = null;
        // 如果拿取索引已到达数组的末尾,则进行重置。
        if (++takeIndex == items.length)
            takeIndex = 0;
        // 递减总数。
        count--;
        if (itrs != null)
            itrs.elementDequeued();
    } else {
        // an "interior" remove
        // 一次内部移除

        // slide over all others up through putIndex.
        //     通过放置索引划过所有其它的(这句话的意思是以移除索引为起点,放置索引为终点,将两者之间的所有的数据向前移动一位
        // ...这因为注释...真服了...)。

        // 移除移除索引位置的数据,并以之为起点,放置索引为终点,将两者之间的数据向前移动一位。
        final int putIndex = this.putIndex;
        for (int i = removeIndex; ; ) {
            // 计算下个索引,
            int next = i + 1;
            // 如果下个索引等于元素数组的长度(即容量),则重置为0。
            if (next == items.length)
                next = 0;
            if (next != putIndex) {
                //     如果下个索引不等于放置索引(这意味着还没有到达迁移的中断),则下个索引位置的元素迁移到当前索引上。并让下个
                // 索引成为新的当前索引。
                items[i] = items[next];
                i = next;
            } else {
                // 如果下个索引等于放置索引,意味着当前索引位置保存的是最后一个需要迁移的数据,将之置为null,并将放置索引前移一位。
                items[i] = null;
                this.putIndex = i;
                break;
            }
        }
        // 递减总数。
        count--;
        // 如果存在迭代器链,则进行遍历其中所有的迭代器,令其根据移除索引对自身进行调整。
        if (itrs != null)
            itrs.removedAt(removeIndex);
    }
    // 唤醒可能被挂起的放置者。
    notFull.signal();
}

    public boolean contains(Object o) —— 包含 —— 判断队列是否包含指定的元素,是则返回true;否则返回false。方法会遍历整个队列,一旦发现相同的元素便返回true,否则返回false。

/**
 * Returns {@code true} if this queue contains the specified element. More formally, returns {@code true} if and only if this queue contains at
 * least one element {@code e} such that {@code o.equals(e)}.
 * 如果队列包含指定元素则返回true。更正式地,当且仅当通过o.equals(e)判断队列至少包含一个(指定)元素时返回true。
 *
 * @param o object to be checked for containment in this queue 队列中检查包含的对象
 * @return {@code true} if this queue contains the specified element  队列包含指定元素则返回true
 * @Description: 包含:判断队列是否包含指定的元素,是则返回true;否则返回false。
 */
@Override
public boolean contains(Object o) {
    // 所有阻塞队列的实现类都是不允许保存null的,因此如果指定元素为null可以直接返回false。
    if (o == null) return false;
    final Object[] items = this.items;
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        if (count > 0) {
            // 以拿取索引为起点,放置索引为终点进行遍历判断指定元素是否存在。
            final int putIndex = this.putIndex;
            int i = takeIndex;
            do {
                if (o.equals(items[i]))
                    return true;
                if (++i == items.length)
                    i = 0;
            } while (i != putIndex);
        }
        return false;
    } finally {
        lock.unlock();
    }
}

    public int drainTo(Collection<? super E> c) —— 流失 —— 用于将队列中的所有元素迁移至指定的集中。该方法底层通过重载的drainTo(Collection<? super E> c, int maxElements)方法实现,其在参数中传入了int类型的最大值,意味着迁移所有的元素。

/**
 * @throws UnsupportedOperationException {@inheritDoc} 不支持操作异常
 * @throws ClassCastException            {@inheritDoc} 类转换异常
 * @throws NullPointerException          {@inheritDoc} 空指针异常
 * @throws IllegalArgumentException      {@inheritDoc} 非法参数异常
 * @Description: 流失:迁移队列中元素至指定的集,并返回迁移的元素数量。
 */
@Override
public int drainTo(Collection<? super E> c) {
    return drainTo(c, Integer.MAX_VALUE);
}

    public int drainTo(Collection<? super E> c, int maxElements) —— 流失 —— 用于将队列中的最多指定数量的元素迁移至指定的集中。该方法的实现也较为简单,先是确定迁移元素的数量(在指定的数量与队列中元素的数量取较小值,由于迁移的元素是在一开始就确定的,因此即使是在元素总数小于指定数量的情况下,后期插入/放置的元素也无法被迁移至数据中),随后遍历队列进行元素的迁移。

    由于该方法同样涉及到元素的移除,因此同样会对迭代器中造成影响。这里分别使用了queueIsEmpty()和takeIndexWrapped(),分别用于分离及清除全部或部分分离的迭代器。这两个方法会在下文详述。

/**
 * @throws UnsupportedOperationException {@inheritDoc} 不支持操作异常
 * @throws ClassCastException            {@inheritDoc} 类转换异常
 * @throws NullPointerException          {@inheritDoc} 空指针异常
 * @throws IllegalArgumentException      {@inheritDoc} 非法参数异常
 * @Description: 流失:迁移队列中指定数量的元素至指定的集,并返回迁移的元素数量。
 */
@Override
public int drainTo(Collection<? super E> c, int maxElements) {
    // 判断参数是否合法。
    checkNotNull(c);
    if (c == this)
        throw new IllegalArgumentException();
    if (maxElements <= 0)
        return 0;
    final Object[] items = this.items;
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        // 确定迁移的最大数量。
        int n = Math.min(maxElements, count);
        int take = takeIndex;
        int i = 0;
        // 以拿取索引为起点,放置索引为终点进行迁移。
        try {
            while (i < n) {
                @SuppressWarnings("unchecked")
                E x = (E) items[take];
                c.add(x);
                items[take] = null;
                if (++take == items.length)
                    take = 0;
                i++;
            }
            return n;
        } finally {
            // Restore invariants even if c.add() threw
            //即使抛出异常也要保证不变量(一致性)。
            if (i > 0) {

                // 重新计算新的总数和拿取索引的位置,并执行迭代器操作。
                count -= i;
                takeIndex = take;
                if (itrs != null) {
                    if (count == 0)
                        itrs.queueIsEmpty();
                    else if (i > take)
                        itrs.takeIndexWrapped();
                }
                // 唤醒所有可能存在的
                for (; i > 0 && lock.hasWaiters(notFull); i--)
                    notFull.signal();
            }
        }
    } finally {
        lock.unlock();
    }
}

    public Object[] toArray() —— 转化数组 —— 用于将队列中的所有元素赋值于一个新的Object(对象)数组中返回。与元素的迁移不同,该方法不会遍历数组,而是会通过拷贝的方式将数据拷贝到指定的数组中。拷贝操作是一个系统级的操作,性能相对逐个迁移来说更高。

    虽说方法toArray()方法返回的是第一个新创建的Object(对象)数组,但在具体的实现中元素并没有使用拷贝的方式赋值,即Object(对象)数组中持有的元素引用与队列持有的元素引用是相同的,在任意一方对元素本身进行修改都会导致另一方的元素出现变化。

/**
 * Returns an array containing all of the elements in this queue, in proper sequence.
 * 返回一个包含队列中所有元素的数组,以恰当的顺序(一般是迭代器的顺序)。
 * <p>
 * The returned array will be "safe" in that no references to it are maintained by this queue.  (In other words, this method must allocate a new
 * array).  The caller is thus free to modify the returned array.
 * 被返回的数组是安全的,队列不包含其任何的引用。(换句话说,该方法必须分配一个新的数组)。调用者可以自由修改返回的数组。(数
 * 组是安全的...但内部的元素可能是同一个,方法并没有强制规定元素的迁移必须是深拷贝模式的)
 * <p>
 * This method acts as bridge between array-based and collection-based APIs.
 * 该方法行为可作为基于数组及基于集实现的API之间的桥梁(即用作两者之间的转换)
 *
 * @return an array containing all of the elements in this queue 一个包含队列所有元素的数组
 * @Description: 转化数组:用于获取一个包含队列所有元素的数组,该数组是新分配的。
 */
public Object[] toArray() {
    Object[] a;
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        // 实例化一个当前元素数量的新数组。
        final int count = this.count;
        a = new Object[count];
        // 通过拷贝的方式进行迁移。
        int n = items.length - takeIndex;
        if (count <= n)
            // 如果放置索引在拿取索引的后方,可一次性拷贝。
            System.arraycopy(items, takeIndex, a, 0, count);
        else {
            // 如果放置索引在拿取索引的前方,需分两次拷贝。
            System.arraycopy(items, takeIndex, a, 0, n);
            System.arraycopy(items, 0, a, n, count - n);
        }
    } finally {
        lock.unlock();
    }
    return a;
}

    public T[] toArray(T[] a) —— 转化数组 —— 用于将队列中的所有元素赋值于一个指定的泛型数组中返回,如果指定泛型数组不足以容纳所有的元素,则新创建一个队列元素数量大小的泛型数组用于容纳后返回。与元素的迁移不同,该方法不会遍历数组,而是会通过拷贝的方式将数据拷贝到指定的数组中。拷贝操作是一个系统级的操作,性能相对逐个迁移来说更高。在拷贝数组之前,其会先判断指定的泛型数组长度是否足够容纳所有的队列元素,如果不足则会新创建一个队列元素大小的泛型数组用于容纳。除此以外,如果指定的泛型数组长度满足要求(>=),但其内部已含有相应的值的话,会在拷贝完毕之后,将数组的后一位设置为null,作为标记值表示真正的元素数据到此为止。

/**
 * Returns an array containing all of the elements in this queue, in proper sequence; the runtime type of the returned array is that of the
 * specified array.  If the queue fits in the specified array, it is returned therein.  Otherwise, a new array is allocated with the runtime type
 * of the specified array and the size of this queue.
 * 返回一个包含队列中所有元素的数组,以恰当的顺序(一般是迭代器的顺序);返回数组的运行时类型为指定数组的运行时类型。如果队
 * 列符合(意思是大小足够容纳所有的队列元素)指定数组,则直接返回。否则需要分配一个指定的数组的运行时类型及队列大小的新数组。
 * <p>
 * If this queue fits in the specified array with room to spare (i.e., the array has more elements than this queue), the element in the array
 * immediately following the end of the queue is set to {@code null}.
 * 如果队列符合指定数组,并且指定数组存在多余空间(也就是这个数组有超过当前集的元素(这个描述并不准确,数组完全可以是空数组,
 * 注释表达的意思应该是指指定数组的长度大于队列中元素的数量)),在数组中的元素紧接着队列结束之后设置为null(即将队列中的元素
 * 全部赋值到数组上后,将后一个数组位置设置为null,作为集元素已经全部赋值完毕的标志位...但这个判断方式也只能在不允许保存null的队
 * 列中使用,恰好阻塞队列也是不可以存null的)。
 * <p>
 * Like the {@link #toArray()} method, this method acts as bridge between array-based and collection-based APIs.  Further, this method
 * allows precise control over the runtime type of the output array, and may, under certain circumstances, be used to save allocation costs.
 * 类似于toArray()方法,该方法行为可作为基于数组及基于集实现的API之间的桥梁(即用作两者之间的转换)。进一步地,这个方法允许
 * 精确支配输出数组的运行时类型,并可能,在某些情况下,用于节约分配成本(类型和大小都与集相符情况下就不需要在分配一个新的数
 * 组了)。
 * <p>
 * Suppose {@code x} is a queue known to contain only strings. The following code can be used to dump the queue into a newly allocated
 * array of {@code String}:
 * 假设x是一个已知质包含字符串的队列。下列代码能够倾倒队列(中的元素)至一个新分配的字符串数组中。
 * <pre> {@code String[] y = x.toArray(new String[0]);}</pre>
 * <p>
 * Note that {@code toArray(new Object[0])} is identical in function to {@code toArray()}.
 * 注意:toArray(new Object[0])的作用与toArray()相等(倒也不能这么说...如果队列中没有元素的时候toArray()会分配新数组,但
 * toArray(new Object[0])不会...说相等也可以,性质相等(equals),但不相同(==))
 *
 * @param a the array into which the elements of the queue are to be stored, if it is big enough; otherwise, a new array of the same runtime
 *          type is allocated for this purpose
 *          用于保存队列的元素的数组(如果足够大);否则会按计划分配一个相同运行时类型的新数组
 * @return an array containing all of the elements in this queue 一个包含队列所有元素的数组
 * @throws ArrayStoreException  if the runtime type of the specified array is not a supertype of the runtime type of every element in this queue
 *                              数组保存异常:如果指定数组的运行时类型不是队列中任意一个元素的运行时类型的父超类
 * @throws NullPointerException if the specified array is null
 *                              空指针异常:如果指定数组为null
 * @Description: 转化数组:用于获取一个包含队列所有元素的泛型数组,该数组可能是传入的数组,也可能是新分配的数组,具体看长度是否
 * @Description: 足够容纳所有的队列元素。如果传入数组的长度过长,会在迁移完毕后将后一位设置为null,作为结束的标记值...可以想得到该
 * @Description: 操作只对不允许存null值的队列有效。
 */
@SuppressWarnings("unchecked")
public <T> T[] toArray(T[] a) {
    final Object[] items = this.items;
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        final int count = this.count;
        final int len = a.length;
        // 判断数组的长度是否足够。如果不够则重新分配一个。由于无法直接常见一个泛型数组,因此该数组是通过反射创建的。
        if (len < count)
            a = (T[]) java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), count);
        int n = items.length - takeIndex;
        // 通过拷贝的方式进行迁移。
        if (count <= n)
            // 如果放置索引在拿取索引的后方,可一次性拷贝。
            System.arraycopy(items, takeIndex, a, 0, count);
        else {
            // 如果放置索引在拿取索引的前方,需分两次拷贝。
            System.arraycopy(items, takeIndex, a, 0, n);
            System.arraycopy(items, 0, a, n, count - n);
        }
        // 如果指定数组的长度更大,则在拷贝结束后将后一位设置为null表示结束。
        if (len > count)
            a[count] = null;
    } finally {
        lock.unlock();
    }
    return a;
}

    public void clear() —— 清除 —— 用于将队列中的所有元素清除。方法会遍历整个元素数组,将其中所有的元素清除。也正式因为方法调用后不会再存在元素,因此其会调用迭代器的queueIsEmpty()方法将所有的迭代器分离。

/**
 * Atomically removes all of the elements from this queue. The queue will be empty after this call returns.
 * 原子地移除队列中的所有元素。队列将在该调用返回后为空。
 *
 * @Description: 清空:清空队列的所有元素。
 */
public void clear() {
    final Object[] items = this.items;
    final ReentrantLock lock = this.lock;
    lock.lock();
    try {
        int k = count;
        if (k > 0) {
            // 以拿取索引为起点,放置索引为终点进行遍历,将所有的元素置null,并还原拿取/放置索引。
            final int putIndex = this.putIndex;
            int i = takeIndex;
            do {
                items[i] = null;
                if (++i == items.length)
                    i = 0;
            } while (i != putIndex);
            takeIndex = putIndex;
            count = 0;
            // 通知迭代器并唤醒所有挂起的放置者。
            if (itrs != null)
                itrs.queueIsEmpty();
            for (; k > 0 && lock.hasWaiters(notFull); k--)
                notFull.signal();
        }
    } finally {
        lock.unlock();
    }
}

    public Iterator iterator() —— 迭代器 —— 用于获取一个Iterator(迭代器)接口对象(下文简称迭代器)。

/**
 * Returns an iterator over the elements in this queue in proper sequence. The elements will be returned in order from first (head) to last (tail).
 * 返回一个按恰当的顺序遍历队列元素的迭代器。元素将按从头到尾的方式保存。
 * <p>
 * The returned iterator is <a href="package-summary.html#Weakly"><i>weakly consistent</i></a>.
 * 返回的迭代器是弱一致性的(即可能迭代到已出队的元素,也可能无法迭代到新入队的元素)。
 *
 * @return an iterator over the elements in this queue in proper sequence 按恰当的顺序遍历队列元素的迭代器
 */
public Iterator<E> iterator() {
    return new Itr();
}

二 Itrs(迭代器链)类源码及机制详解


 类

    Itrs(迭代器链)类是ArrayBlockingQueue(数组阻塞队列)类的一个内部类,以链表的形式保存所有未分离的迭代器。其一方面是迭代器的仓库,另一方面也是队列与迭代器之间沟通的桥梁。当队列的数据发生变化影响到迭代器时,会通过Itrs(迭代器链)类对象(下文简称迭代器链)对所有的未分离迭代器进行访问,并根据实际情况对迭代器进行和清理。

    迭代器链中虽然保存了所有的未分离迭代器,但不代表其只包含未分离迭代器。迭代器链并不能保证一个被分离的迭代器被实时移除,因此迭代器可能残留着分离迭代器。之所以如此是因为保证迭代器链的绝对“纯净”会对性能有着较大的影响,因此绝大程度下进行的都是算法清理而不是全局清理。所谓算法清理,即在上个失效节点(包含但不限于容纳分离迭代器的节点)的基础上继续向后遍历一定的距离。这种情况下往往就无法完整遍历,这便是迭代器链会残留分离迭代器的原因。但话虽如此,实际上只要是队列的移除操作(移除/拿取、内部移除、迭代移除等)都会导致对迭代器的影响。如此一来就需要遍历所有的迭代器以进行调整,此时也会顺势进行迭代器的清理工作。此时的清理是可以保证“纯净”的,即清理后不会残留失效迭代器。

/**
 * Shared data between iterators and their queue, allowing queue modifications to update iterators when elements are removed.
 * 共享迭代器与其队列之间的数据,当元素移除时允许队列修改更新迭代器(迭代器链即是迭代器的容器,也是队列与迭代器之间沟通的桥
 * 梁。一旦队列的数据/结构发生改变,会通过迭代器链对所有的迭代器进行修改)。
 * <p>
 * This adds a lot of complexity for the sake of correctly handling some uncommon operations, but the combination of circular-arrays and
 * supporting interior removes (i.e., those not at head) would cause iterators to sometimes lose their places and/or (re)report elements
 * they shouldn't. To avoid this, when a queue has one or more iterators, it keeps iterator state consistent by:
 * (迭代器链的存在)正确地处理一些不常规的操作(且)新增了许多复杂性(确实挺复杂的...),但循环数组与支持内部移除的组合会导
 * 致迭代器有时丢失它们的位置和/或(重新)报告它们不应该报告的元素。为了避免这些,当一个队列拥有一个或更多迭代器,它(队列)
 * 通过以下方式保证迭代器状态的一致性。
 * <p>
 * (1) keeping track of the number of "cycles", that is, the number of times takeIndex has wrapped around to 0.
 * (1) 保持循环的数量的足迹,即拿取索引环绕0的次数(即拿取索引重置为0的次数)。
 * (2) notifying all iterators via the callback removedAt whenever an interior element is removed (and thus other elements may be shifted).
 * (2) 当一个内部元素移除时通知所有迭代器回调removedAt()方法(内部元素的移除会导致其后继在数组上的迁移,这就会造成保存在迭代
 * 器中的下个元素和下个索引不符合的情况)。
 * <p>
 * These suffice to eliminate iterator inconsistencies, but unfortunately add the secondary responsibility of maintaining the list of iterators.
 * We track all active iterators in a simple linked list (accessed only when the queue's lock is held) of weak references to Itr.  The list is cleaned
 * up using 3 different mechanisms:
 * 这足够排除迭代器矛盾,但是不幸地是新增了确定迭代器的列表的次要责任。我们在一个对Itr弱引用的简单链接列表中追踪所有的活动迭代器
 * (只在队列的锁被持有时访问)。这个列表使用三个不同的机制进行清理。
 * <p>
 * (1) Whenever a new iterator is created, do some O(1) checking for stale list elements.
 * (1) 当一个新迭代器被创建时,检查过期列表元素。
 * <p>
 * (2) Whenever takeIndex wraps around to 0, check for iterators that have been unused for more than one wrap-around cycle.
 * (2)当拿取索引环绕0时(即重置为0)时,检查超过一次环绕循环未使用的迭代器。
 * <p>
 * (3) Whenever the queue becomes empty, all iterators are notified and this entire data structure is discarded.
 * (3) 当队列变为空时,所有迭代器被通知以及全部数据结构被遗弃(即被设置为分离模式)。
 * <p>
 * So in addition to the removedAt callback that is necessary for correctness, iterators have the shutdown and takeIndexWrapped callbacks
 * that help remove stale iterators from the list.
 * 因此,除了正确使用removedAt回调函数外,迭代器还有shutdown和takeIndexWrapped回调函数,可以帮助从列表中删除过时的迭代器。
 * <p>
 * Whenever a list element is examined, it is expunged if either the GC has determined that the iterator is discarded, or if the iterator reports
 * that it is "detached" (does not need any further state updates).  Overhead is maximal when takeIndex never advances, iterators are discarded
 * before they are exhausted, and all removals are interior removes, in which case all stale iterators are discovered by the GC. But even in this
 * case we don't increase the amortized complexity.
 * 当一个列表已被检查,如果GC确定该迭代器是废弃的(即迭代器已被GC回收),或如果报告是分离的(不需要任何进一步的状态更新)则(迭
 * 代器)就会被删除。当拿取索引永不前进,迭代器在耗尽之前被丢弃以及所有的移除都是内部移除时,开销是最大的,在这种情况下,所有过期
 * 的迭代器都会被GC发现。但即使在这种情况下我们也没有增加平摊复杂度。
 * <p>
 * Care must be taken to keep list sweeping methods from reentrantly invoking another such method, causing subtle corruption bugs.
 * 必须注意防止列表扫描方法重入调用另一个这样的方法,从而导致微妙的损坏错误。
 *
 * @Description: 迭代器链:持有着可用的迭代器。即是迭代器的仓库,也是队列与迭代器之间的数据共享的桥梁。
 */
class Itrs {
    ...
}

 字段

    cycles(循环) —— 用于记录队列拿取索引重置的次数,当队列重置拿取索引时,该值会被更新。该值的直接作用是作为计算迭代器两次操作之间拿取索引前进距离或与之相似计算(也可能是用来计算其它值,但都与拿取索引的前景距离相似)的条件,而更核心的作用则是将计算得到的值作为迭代器调整的判断依据,例如是否将迭代器分离,是否需要对迭代器的某些值进行更新等。

/**
 * Incremented whenever takeIndex wraps around to 0
 * 当拿取索引环绕0(重置)时增长
 *
 * @Description: 循环:用于记录队列拿取索引重置的次数,当队列重置拿取索引是,该值会被更新。
 */
int cycles = 0;

    head(头) —— 用于持有迭代器链头节点(是迭代器的容器,也是迭代器链的基本组成单位)的引用。

/**
 * Linked list of weak iterator references
 * 弱迭代器引用链表
 *
 * @Description: 头:用于持有迭代器链头节点的引用。
 */
private Node head;

    sweeper(清洁工) —— 用于持有清洁工的引用。清洁工的本质是上轮算法清理扫描到的最后一个未失效节点(所谓的失效节点指的是迭代器分离的节点和迭代器不存在的节点,而未失效节点则与之相反)。

/**
 * Used to expunge stale iterators
 * 用于删除失效迭代器
 *
 * @Description: 清洁工:用于持有清洁工节点的引用。清洁工的本质是上一次内部结束的清理扫描到的最后一个未失效的节点。
 */
private Node sweeper = null;

    SHORT_SWEEP_PROBES(短清洁探针)/LONG_SWEEP_PROBES(长清洁探针) —— 分别记录着两个常数,被作为算法清理中在清洁工的基础上继续向后遍历的距离。根据不同的选择,向后遍历的距离也会不同。但实际上真正可以控制的只有首次的查找距离,一旦在首次查找中发现了失效节点,则后续的查找距离就固定为长清洁探针(16)。

/**
 * @Description: 短/长清洁探针
 */
private static final int SHORT_SWEEP_PROBES = 4;
private static final int LONG_SWEEP_PROBES = 16;

 构造方法

    Itrs(Itr initial) —— 创建一个包含指定迭代器的迭代器链,指定迭代器会被作为头节点,具体操作由register(Itr itr)方法实现。

Itrs(Itr initial) {
    // 将传入的迭代器为基础创建第一个节点作为头节点。
    register(initial);
}

 方法

    void register(Itr itr) —— 注册 —— 向迭代器链中新增一个迭代器,该迭代器会被作为新的头节点,因此可知迭代器链采用的是头插法。

/**
 * Adds a new iterator to the linked list of tracked iterators.
 * 新增一个迭代器至追踪迭代器链表中。
 *
 * @Description: 注册:将一个新的迭代器加入迭代器链中。
 */
void register(Itr itr) {
    // assert lock.getHoldCount() == 1;
    // 断言lock.getHoldCount() == 1(即当前锁被持有);

    // 以传入的迭代器为基础创建一个新节点,并将新节点的后继赋值为头节点,最后将头节点赋值为新节点。因此可知迭代器链式采用的是
    // 头插法。
    head = new Node(itr, head);
}

    void doSomeSweeping(boolean tryHarder) —— 执行一些清理 —— 用于对迭代器链进行一定程度上的清理,但不一定会清理完全。该方法会在新的迭代器创建时或迭代器分离时调用。该方法即所谓的算法清理,在上个失效节点的基础上向后遍历指定的距离。如果在这段距离内存在其它的失效节点,则将之清理以之为起点继续向后;否则宣告清理结束,等待下次清理。算法清理有助于提升性能,避免操作频繁触发迭代器链的全局清理,但代价是可能才残留一定的失效节点。

    清理的起点是从迭代器链的头节点或清洁工。头节点相比很容易理解,即从头开始清理迭代器链,而清洁工则是上轮清理留下的产物。如果上轮清理在迭代器链的内部停止,即没有完成的清理到迭代器链的尾部,则会将清洁工赋值为最后一个遍历到的未失效节点,以作为下次算法清理的起点。我们可以选择向后遍历的距离,但是只对首次有效,即我们可以选择自起点开始的首次遍历长度。根据tryHarder(尝试加把劲)参数的不同,会分别遍历短清洁探针(4)或者长清洁探针(16)的长度,如果在首次遍历中发现了失效节点,则后续的遍历都将以长清洁探针(16)作为遍历的距离。

    算法清理的效果极致是完成一次全局清理。如果迭代器链中真的包含很多的失效节点,那么理论上算法清理也是可以完成一次全局清理的。但这之中存在一个问题,即算法清理可能是从迭代器链的内部(即清洁工)开始的,因此即使清洁到了迭代器链的尾部,也无法保证完全清理完全,因为没有清理清洁工之前的失效节点。对于这种情况,算法清理的处理方式是如果此次清理的起点是清洁工,则在遍历到尾节点后会重新从迭代器链的头部再次遍历清理(前提是还没有达到指定的遍历距离)。而如果这一次又遍历到了尾节点,则不会再从重置,而是直接结束清理,因为已经达到了一次全局清理的机制效果。

/**
 * Sweeps itrs, looking for and expunging stale iterators. If at least one was found, tries harder to find more. Called only from iterating thread.
 * 清理迭代器链,查找并删除失效迭代器。如果至少找到一个,尝试加把劲找到更多。(该方法)只通过迭代线程调用
 *
 * @param tryHarder whether to start in try-harder mode, because there is known to be at least one iterator to collect
 *                  是否开始加把劲模式,因为已知最少有一个迭代器要收集
 * @Description: 执行一些清理:用于对迭代器链进行一定程度上的清理,但不一定会清理完全。该方法会在新的迭代器创建时或迭代器分离时调用。
 */
void doSomeSweeping(boolean tryHarder) {
    // assert lock.getHoldCount() == 1;
    // 断言lock.getHoldCount() == 1(即锁正在被线程持有);
    // assert head != null;
    // 断言head != null(即迭代器链中存在头节点);

    // 根据加入的参数选择是否开启加把劲模式,所谓的加把劲模式其实就是选择第一次清理的查询长度。即第一次查找的最大幅度。非加把劲模式
    // 为4,而加把劲模式为16。
    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
    // 将搜索限制为一次完整扫描(即拿取索引到放置索引之间的范围)

    if (sweeper == null) {
        // 如果清洁工(快照)为null,意味着此次清理是从迭代器链头开始的,满足一次清理整个迭代器链的前提条件,因此会被限制为一次清理。
        o = null;
        p = head;
        passedGo = true;
    } else {
        // 如果清洁工(快照)不为null,意味着此次清理是从迭代器链内部开始的,不满足一次清理整个迭代器链的前提条件(起点前一段的迭代器
        // 链是清理不到的),因此不会被限制为一次清理,即到达迭代器链的尾部后会从头开始,而这一次的清理会被限制为一次清理。
        o = sweeper;
        p = o.next;
        passedGo = false;
    }
    // 循环清理。
    for (; probes > 0; probes--) {
        // p == null说明清理已经到了迭代器链的最后。
        if (p == null) {
            // 如果被限制一次清理,则直接退出。
            if (passedGo)
                break;
            // 如果没有被限制一次清理,则从头继续开始,但此次清理会被限制为一次清理,因此一次清理最多只会到达链表尾部两次。看到这里作者
            // 的目的其实已经很明显了,就是为了对迭代器链进行从头到尾的完整清理。那为什么不从头到尾的进行一次清理还要限制清理的范围呢?个
            // 人猜测应该是为了性能考虑,如此一来就会使得清理可能在迭代器链的中间停止。而后一次的清理必须在上一次清理的基础上进行,否则迭
            // 代器链后方的失效节点将可能永远都无法被清理。但如此一来,到清理掉迭代器链后段的失效节点后,迭代器链前端的失效节点又无法清理(
            // 因为此次清理是从迭代器链的内部开始的)。为了处理这一点,从迭代器链内部开始的清理会被允许再次从头开始,也就是不限制一次清理,
            // 以使得其可以从头部开始再次清理。
            o = null;
            p = head;
            passedGo = true;
        }
        // 获取当前节点的迭代器与后继节点。
        final Itr it = p.get();
        final Node next = p.next;
        // 如果迭代器为null或者迭代器是分离模式,则说明当前节点是一个失效节点,需要被清理。
        if (it == null || it.isDetached()) {
            // found a discarded/exhausted iterator
            // 找到一个分离/耗尽的迭代器(即失效的迭代器)

            // 既然找到了节点,这就很大程度上意味着后续存在更多的失效节点,应该将递减的扫描长度还原,继续向后遍历。
            probes = LONG_SWEEP_PROBES; // "try harder"
            // unlink p
            // 断开p的链接。

            // 请当前节点的迭代器(所指对象清除),因为失效节点不仅只以断开所指对象链接的节点,还有处于分离状态的节点。
            p.clear();
            // 将节点从迭代器链中移除,即常规的节点断开重连操作。
            p.next = null;
            if (o == null) {
                head = next;
                if (next == null) {
                    // We've run out of iterators to track; retire
                    // 我们已经没有迭代器可以跟踪了;退休

                    // 如果迭代器链中已经没有任何的节点了,则说明所有的迭代器都已经被回收了,将迭代器链置null,并结束清理。
                    itrs = null;
                    return;
                }
            } else
                o.next = next;
        } else {
            // 如果当前节点不是失效节点,则继续向下遍历。
            o = p;
        }
        p = next;
    }
    // 当清理完成后,如果此次清理不是因为完全清理而结束,则将清洁工赋值为最后一个扫描到的非失效节点,作为下一次清理的起点。
    this.sweeper = (p == null) ? null : o;
}

    void takeIndexWrapped() —— 拿取索引包装 —— 更新循环(即拿取索引的重置次数),并将长期未使用的迭代器强制分离,并顺势清理迭代器链中已失效的迭代器。该方法会在拿取索引重置时会调用,以更新迭代器链中保存的循环。循环会被作为判断迭代器是否长期未使用的依据,一旦被判定,则迭代器将被强制分离,这是数组阻塞队列避免迭代器的弱一致性过于弱的一种保护机制。该具体逻辑在迭代器的takeIndexWrapped()方法中完成,与当前方法同名,具体会在迭代器中详解。

    由于迭代器是否长期未使用是针对迭代器链中所有未分离迭代器的,因此会遍历整个迭代器链,此时会顺势进行失效节点的清理。

/**
 * Called whenever takeIndex wraps around to 0.
 * 当拿取索引环绕0时(即重置时)调用
 * <p>
 * Notifies all iterators, and expunges any that are now stale.
 * 通知所有的迭代器,并且删除任意当前失效(的迭代器)。
 *
 * @Description: 拿取索引包装:用于更新拿取索引的重置次数,并顺势清理迭代器链中已失效的迭代器。
 */
void takeIndexWrapped() {
    // assert lock.getHoldCount() == 1;
    // 断言加锁。

    // 递增循环,表示拿取索引的重置次数又增加了一次。
    cycles++;
    // 顺势遍历迭代器链,将其中已失效的迭代器删除。
    for (Node o = null, p = head; p != null; ) {
        final Itr it = p.get();
        final Node next = p.next;
        if (it == null || it.takeIndexWrapped()) {
            // 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;
}

    void removedAt(int removedIndex) —— 移除所在 —— 根据移除索引对迭代器链中所有的未分离迭代器进行调整,并顺势清理失效节点。该方法在内部移除元素时调用,遍历整个迭代器链,通过迭代器的同名removedAt(int removedIndex)方法对迭代器进行调整。调整迭代器是一个相对复杂的过程,此处暂不提及,会在下文讲解迭代器是详述。由于会遍历整个迭代器,照例,此时会清理所有的失效节点。

/**
 * Called whenever an interior remove (not at takeIndex) occurred.
 * 当一个迭代器发生移除(不在拿取索引上)时调用。
 * <p>
 * Notifies all iterators, and expunges any that are now stale.
 * 通知所有的迭代器,并删除尽可能多当前失效的(迭代器)。
 *
 * @Description: 移除所在:将指定索引的元素从迭代器中移除
 */
void removedAt(int removedIndex) {
    // 遍历整个迭代器链。
    for (Node o = null, p = head; p != null; ) {
        // 如果节点的迭代器已被GC回收或者迭代器
        final Itr it = p.get();
        final Node next = p.next;
        if (it == null || it.removedAt(removedIndex)) {
            // 如果迭代器已被GC回收,或者迭代器在根据移除索引进行调整后被切换为了分离模式,则节点从迭代器链中移除。
            // 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;
    }
    // 如果清除完成后所有的迭代器都被清除,则接迭代器链置null。
    if (head == null)   // no more iterators to track
        itrs = null;
}

    void queueIsEmpty() —— 队列为空 —— 将迭代器链中的所有迭代器强制分离,并清除迭代器链。该方法在移除/拿取元素数组中的最后一个元素时调用,由于调用后元素数组中已不存在元素,因此判断当前所有的未分离迭代器都作废(其实想继续用也是可以的,只要后续插入/放置了新的元素,且没有超过循环限制)。具体做法是遍历的迭代器链,将所有的未分离迭代器都强制分离,最后将迭代器链置null。

/**
 * Called whenever the queue becomes empty.
 * 当队列为空时调用。
 * <p>
 * Notifies all active iterators that the queue is empty, clears all weak refs, and unlinks the itrs datastructure.
 * 通知所有活动迭代器该队列为空,清除所有弱引用,并断开所有迭代器数据结构的链接。
 *
 * @Description: 队列为空:用于清除所有现存的迭代器。
 */
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();
        }
    }
    // 直接将整个迭代器链置null。
    head = null;
    itrs = null;
}

    void elementDequeued() —— 元素出队 —— 该方法当队列中不存在元素时,会调用queueIsEmpty()方法强制分离所有的未分离迭代器;而当拿取索引重置时,会调用takeIndexWrapped()方法更新循环,并清除迭代器链中的分离迭代器。

/**
 * Called whenever an element has been dequeued (at takeIndex).
 * 当一个元素从拿取索引位置出队时调用
 *
 * @Description: 元素出队:
 */
void elementDequeued() {
    // assert lock.getHoldCount() == 1;
    // 断言加锁。
    if (count == 0)
        // 如果总数为0,说明队列中所有的元素都已移除/拿取成为了一个空队列,此时应该将迭代器链中所有的迭代器清除,并将每个迭代器都
        // 切换为分离模式。
        queueIsEmpty();
    else if (takeIndex == 0)
        // 如果拿取索引 == 0,意味着拿取索引又经历了一次充值。我们需要在迭代器链中更新这个次数(cycles),其被用于计算同一个迭代器
        // 两次操作之间的拿取索引的距离,更深层次的作用是作为迭代器索引是否失效,是否需切换为分离模式的判断依据。
        takeIndexWrapped();
}

三 Node(节点)类源码及机制详解


 类

    Node(节点)类是Itrs(迭代器链)类的内部类,被作为迭代器的容器,同时也是迭代器链的基本单位。Node(节点)类其最大的特点在于其继承自WeakReference(弱引用)类,因此其实例也是WeakReference(弱引用)类对象。迭代器被作为其所指对象,因为可知当迭代器失去外部引用时便会被GC回收,故而此时的节点就会变为一个空节点,即不包含迭代器的节点。空节点与迭代器分离的节点两者都属于失效节点的范畴。

/**
 * Node in a linked list of weak iterator references.
 * 弱迭代器引用链表的一个节点。
 *
 * @Description: 节点类:这个一个弱引用类,以迭代器作为其所指对象,一旦迭代器失去了外部引用被GC回收后,节点就会变成一个失效节
 * @Description: 点,即没有迭代器的节点。
 */
private class Node extends WeakReference<Itr> {
    ...
}

 字段

    next(下个) —— 用于持有其后继节点的引用。

/**
 * @Description: 后继:用于持有迭代器链中后继节点的引用
 */
Node next;

 构造方法

    Node(Itr iterator, Node next) —— 创建一个包含指定迭代器的节点,并将传入的节点作为其后继节点。迭代器的设置通过调用父类的构造方法完成,由于Node(节点)类继承自WeakReference(弱引用)类,因此可知迭代器即是其所指对象。

Node(Itr iterator, Node next) {
    super(iterator);
    this.next = next;
}

四 Itr(迭代器)类源码及机制详解


 类

    Itr(迭代器)类是ArrayBlockingQueue(数组阻塞队列)类内部自实现的一个迭代器,继承自Iterator(迭代器)接口。其是弱一致性的迭代器,会在自身持有当前迭代元素(即调用next()方法返回的元素)的快照。因此即使当前迭代元素已从队列中移除,也依然可以保证迭代继续进行。

/**
 * Iterator for ArrayBlockingQueue.
 * 数组阻塞队列的迭代器
 * <p>
 * To maintain weak consistency with respect to puts and takes, we read ahead one slot, so as to not report hasNext true but then not have
 * an element to return.
 * 为对于插入/放置及移除/拿取保持弱一致性,我们提前读取一个槽,免得hasNext()方法报告为true但没有元素返回(的情况)(即先将下个
 * 元素保存在迭代器中,这样即使被移除/拿取也可能保证返回。这么做也可能会使得无法迭代到新加入的元素)。
 * <p>
 * We switch into "detached" mode (allowing prompt unlinking from itrs without help from the GC) when all indices are negative, or when
 * hasNext returns false for the first time.  This allows the iterator to track concurrent updates completely accurately, except for the corner
 * case of the user calling Iterator.remove() after hasNext() returned false.  Even in this case, we ensure that we don't remove the wrong
 * element by keeping track of the expected element to remove, in lastItem.  Yes, we may fail to remove lastItem from the queue if it moved due
 * to an interleaved interior remove while in detached mode.
 * 当所有索引都为负时,或者当hasNext第一次返回false时,我们切换到"分离"模式(允许在没有GC帮助的情况下提示从itrs断开链接)。这允许
 * 迭代器完全准确地跟踪并发更新,除非用户在hasNext()返回false后调用iterator .remove()。即使在这种极端情况下,我们也可以在lastItem中
 * 跟踪要删除的预期元素,以确保不会删除错误的元素。是的,如果lastItem在分离模式下由于交叉的内部删除而移动,我们可能无法从队列中删
 * 除它。
 */
private class Itr implements Iterator<E> {
    ...
}

 字段

    cursor(游标) —— 用于记录下下个元素的索引值。之所以要使用游标来记录下下索引值是因为为了避免下个索引与下个元素的不同步(即避免下个索引在元素数组中对应的元素与下个元素不一致),下个索引会在其内部移除时(即下下个索引在元素数组中对应的元素被移除)被赋值为REMOVED(-2)。如此一来就无法以自身为基础来获取下下个索引,因此需要借助游标来提前保存。当元素数组已不存在可用于迭代的元素时,游标会被赋值为NONE(-1)。

/**
 * Index to look for new nextItem; NONE at end
 * 用于查找新下个元素的索引,NONE(-1)结束
 *
 * @Description: 游标:用于查找新下个元素的索引,即下下个元素的索引。之所以在存在下个索引的情况下还要存在一个游标,是因为无论是
 * @Description: 下个索引还是上个索引,其在元素数组中对应的元素都可能被中途移除,这种情况下其会被标记为特殊值,从而无法继续向后迭
 * @Description: 代,因此需要实现探查到下下个迭代的索引。游标与上/下个索引不同,即使该位置的元素被中途移除了,也只会重新定位,除
 * @Description: 非已经没有可迭代的元素了。
 */
private int cursor;

    nextItem(下个元素) —— 保存下次迭代(即调用next()方法)所返回的元素。该字段保存着下个索引在元素数组中对应的元素,可以将之理解为快照。也正是因为如此,即使元素数组中该值被移除(移除/拿取及内部移除),迭代器也依然在下次迭代中正常返回元素,实现了迭代器的弱一致性。

/**
 * Element to be returned by next call to next(); null if none
 * 下次调用next()方法返回的元素;如果没有则为null。
 *
 * @Description: 下个元素:即下次调用next()方法返回的元素。
 */
private E nextItem;

    nextIndex(下个索引) —— 保存下次迭代返回元素的索引。当迭代器执行下次迭代后如果发现游标为NONE(-1),则说明迭代器已经没有可迭代的元素了,则下个索引也会被赋值为NONE(-1)。如果下个索引在元素数组中对应的元素被移除(移除/拿取及内部移除),则下个索引会被赋值为REMOVED(-2)。但话虽如此,移除/拿取与内部移除的赋值时间却不相同。如果是移除/拿取,则下个索引在对应元素移除/拿取时并不会发生变化,而是等到迭代器执行操作时(hasNext()及next()等)进行调整;而如果是内部移除,则会在内部移除发生时赋值。两者的赋值时间之所以不同是因为正常的移除/拿取并不会导致元素发生迁移,因此最多只会导致迭代器中保存的索引值失效而不是异常。这种失效完全可以在指定迭代器执行操作时单独处理,即定位新的索引或将之分离,从而避免针对迭代器链中所有的未分离迭代其执行操作而产生的性能问题;而内部移除则因为会导致元素发生迁移的原因导致迭代器索引异常,并且一旦没有在内部移除时立即处理,则后续处理将无法保证正确性,因此必须立即执行调整,也就是上文提及的迭代器链的removedAt(int removedIndex)方法。

    所谓内部移除时元素迁移导致迭代器索引异常,可以理解为索引错位。其中最简单的情况就是如果内部移除一个早已迭代的元素,由于元素迁移的原因,迭代器记录的各个索引也应该前移一位,否则下下次迭代到的元素就是原下下个元素的后继元素(或者更后面)。当然,除此以外还有其它场景。

/**
 * Index of nextItem; NONE if none, REMOVED if removed elsewhere
 * 下个元素的索引;如果没有则为NONE(-1),如果移到别处(迁移)则为REMOVED(-2)。
 *
 * @Description: 下个索引:下个元素的索引,如果已没有下个元素则为NONE(-1),如果下个元素被转移则为REMOVED(-2)。
 */
private int nextIndex;

    lastItem(上个元素) —— 保存上个迭代的元素。上个元素在一般情况下没有作用,其只用于迭代器迭代完毕后被分离时兼容迭代移除操作,即remove()方法。这一点会在下文详述。

/**
 * Last element returned; null if none or not detached.
 * 返回上个元素;如果没有或没有分离则为null。
 *
 * @Description: 上个元素:即当前线程可见的最后一个元素。
 */
private E lastItem;

    lastRet(上个索引) —— 保存上个迭代元素的索引。当迭代成功执行后,上个索引会被赋值为下个索引。该值用于支持迭代器的remove()操作。

/**
 * Index of lastItem, NONE if none, REMOVED if removed elsewhere
 * 上个元素的索引,如果没有则为NONE(-1),如果移到别处(迁移)则为REMOVED(-2)。
 *
 * @Description: 上个索引:上个元素的索引,如果已没有上个元素素则为NONE(-1),如果上个元素转移则为REMOVED(-2)。
 */
private int lastRet;

    prevTakeIndex(上个拿取索引) —— 用于保存迭代器上次操作时队列的拿取索引值。当迭代器执行某些操作的时候会将当时队列的拿取索引保存下来,直接作用是在下次操作的时候计算拿取索引的前进距离(迭代器的两次操作之间队列可能会有出/入队操作)。更深层次的作用是将计算出来的前进距离将作为调整迭代器的判断依据。

/**
 * Previous value of takeIndex, or DETACHED when detached
 * 上个拿取索引的值,或当分裂是为DETACHED(-3)
 *
 * @Description: 上个拿取索引:用于保存拿取索引的快照。当迭代器执行某些操作的时候会将当时队列的拿取索引保存下来,作用是在下次操作
 * @Description: 的时候计算拿取索引的前进距离(迭代器的两次操作之间队列可能会有出/入队操作)。计算出来的前进距离将作为调整迭代器的
 * @Description: 判断依据。
 */
private int prevTakeIndex;

    prevCycles(上个循环) —— 用于保存迭代器上次操作时迭代器链的循环值。当迭代器执行某些操作的时候会将当时迭代器链的循环保存下来,直接作用是在下次操作的时候计算拿取索引的前进距离(迭代器的两次操作之间队列可能会有出/入队操作)。更深层次的作用是将计算出来的前进距离将作为调整迭代器的判断依据。

/**
 * Previous value of iters.cycles
 * 上个iters.cycles的值。
 *
 * @Description: 上个循环:用于保存拿取索引的重置次数的快照。当迭代器执行某些操作的时候会将迭代器链中的保存的循环(即拿取索引的重置
 * @Description: 次数)保存下来,作用是在下次操作的时候计算拿取索引的前进距离(迭代器的两次操作之间队列可能会有出/入队操作)。计算出
 * @Description: 来的前进距离将作为调整迭代器的判断依据。
 */
private int prevCycles;

    NONE(无) —— 表示无可用元素的特殊值。

/**
 * Special index value indicating "not available" or "undefined"
 * 表示"无可用"或"未定义"的特殊索引值
 *
 * @Description: 无:表示不存在。
 */
private static final int NONE = -1;

    REMOVED(移除) —— 表示原索引在元素数组中对应的值已被移除的特殊值。

/**
 * Special index value indicating "removed elsewhere", that is, removed by some operation other than a call to this.remove().
 * 特殊的索引值表示“从别处移除”,也就是说,通过调用this.remove()以外的某些操作移除。
 *
 * @Description: 移除:表示指定位置的元素已被移除。
 */
private static final int REMOVED = -2;

    DETACHED(分离) —— 表示迭代器已分离的特殊值。

/**
 * Special value for prevTakeIndex indicating "detached mode"
 * 表示分离模式的"上个拿取索引"的特殊值。
 *
 * @Description: 分离:表示分离模式的"上个拿取索引"的特殊值。
 */
private static final int DETACHED = -3;

 构造方法

    Itr() —— 用于创建一个队列的迭代器。根据迭代器创建是队列的情况不同,迭代器的状态与后续操作也不相同。如果迭代器创建时队列中没有任何元素,则该迭代器默认就是分离的,也不会被加入迭代器链中,即该迭代器没有任何实际意义;如果创建时存在元素,则会创建一个真正可用的迭代器,并会将该迭代器加入迭代器链中,随后触发对迭代器链的算法清理。

Itr() {
    // assert lock.getHoldCount() == 0;
    // 断言lock.getHoldCount() == 0(即锁没有被持有)。

    // 赋值上个索引为NONE(-1),表示不存在。
    lastRet = NONE;
    // 加锁。
    final ReentrantLock lock = ArrayBlockingQueue.this.lock;
    lock.lock();
    try {
        if (count == 0) {
            // assert itrs == null;
            // 断言itrs == null(即迭代器链为null);

            // 由于队列中不存在元素,因此将游标及下个索引赋值为NONE(-1),表示不存在后续元素。将上个拿取索引赋值为DETACHED(-2),
            // 表示当前为分离模式。并且由于不存在元素,该迭代器也没有迭代的必要,不需要加入迭代器链中。
            cursor = NONE;
            nextIndex = NONE;
            prevTakeIndex = DETACHED;
        } else {
            // 如果队列中存在元素,则将上个拿取索引赋值为当前拿取索引。
            final int takeIndex = ArrayBlockingQueue.this.takeIndex;
            prevTakeIndex = takeIndex;
            // 将下个索引赋值为拿取索引,并通过下个索引获取到下个元素。如果就将索引与对应的元素都保存在了迭代器中,即使元素从队列中被移除
            // 也依然可以被访问到。
            nextItem = itemAt(nextIndex = takeIndex);
            // 获取游标。即下下个索引的值,如果到达元素数组的尾部会重置,如果到达放置索引的位置这说明已经到了终点,没有元素了,会被赋值为
            // NONE(-1)。
            cursor = incCursor(takeIndex);
            if (itrs == null) {
                // 如果不存在迭代器链,则创建第一个可迭代的迭代器作为迭代器链的头节点。
                itrs = new Itrs(this);
            } else {
                // 将当前迭代器加入迭代器链中。
                itrs.register(this);
                // in this order
                // 按这个顺序(啥顺序)?

                // 对迭代器链进行一定程度的清理。默认是不开启加把劲模式的,也就是第一次扫描只会扫描4个节点。
                itrs.doSomeSweeping(false);
            }
            // 将上个循环赋值为迭代器链的循环。
            prevCycles = itrs.cycles;
            // assert takeIndex >= 0;
            // 断言 拿取索引 >= 0;
            // assert prevTakeIndex == takeIndex;
            // 断言 上个拿取索引==  拿取索引;
            // assert nextIndex >= 0;
            // 断言 下个索引 >=  0;
            // assert nextItem != null;
            // 断言 下个元素 不为空;
        }
    } finally {
        lock.unlock();
    }
}

 方法

    boolean isDetached() —— 是否分离 —— 判断迭代器是否分离。直接判断上个拿取索引是否小于0,如果迭代器被分离,该字段会被赋值为DETACHED(-3)。

/**
 * @Description: 是否分离:判断当前迭代器是否处于分离模式。
 */
boolean isDetached() {
    // assert lock.getHoldCount() == 1;
    // 断言lock.getHoldCount() == 1(即当前锁处于被线程只有的状态);

    // 判断上个拿取索引是否小于0,这意味迭代器处于分离模式。
    return prevTakeIndex < 0;
}

    private int incCursor(int index) —— 递增游标 —— 用于循环递增传入的索引。方法的名字虽然是递增游标,但实际上是递增可以用于递增所有索引。当索引值等于容量时说名以到达队列的末端,按循环数组的特性,需要重新从0开始递增。递增后的索引如果等于放置索引,则说明已经元素数组中以没有元素可读取,返回特殊值NONE(-1)。

/**
 * @Description: 递增游标:用于获取下个存在元素的索引位置。
 */
private int incCursor(int index) {
    // assert lock.getHoldCount() == 1;
    // 断言lock.getHoldCount() == 1(即当前锁处于被线程只有的状态);

    // 如果索引递增后等于容量,则说明已经元素数组的尾部,需要重置。
    if (++index == items.length)
        index = 0;
    // 递增后的索引等于放置索引,则说明已经到达了终点,不存在后继元素了,因此需要将之赋值为NONE(-1)。
    if (index == putIndex)
        index = NONE;
    return index;
}

    private boolean invalidated(int index, int prevTakeIndex, long dequeues, int length) —— 失效 —— 判断索引是否失效,失效返回true;否则返回false。所谓的索引失效,是指索引已不处于当前元素数组的元素段中,即索引位于拿取索引之前(不是单纯的索引小于拿取索引,需要结合循环数组的重置概念)。如果索引位于拿取索引之前,则其就是失效的;否则就是有效的。

/**
 * Returns true if index is invalidated by the given number of dequeues, starting from prevTakeIndex.
 * 如果索引从prevTakeIndex开始按指定的出列数无效,则返回true。
 *
 * @Description: 失效:判断索引是否失效,失效返回true;否则返回false。
 */
private boolean invalidated(int index, int prevTakeIndex, long dequeues, int length) {
    // 如果传入的索引小于0,则说明其本身已被赋予标志位,可看做有效值,返回false。
    if (index < 0)
        return false;
    // 计算指定索引到上个拿取索引的距离。如果计算的结果小于0,则说明上个拿取索引在指定索引的后面。故而在计算记过的基础上加上容量,得
    // 到的结果等价于(prevTakeIndex - index)。
    int distance = index - prevTakeIndex;
    if (distance < 0)
        distance += length;
    // 如果拿取索引相比上个拿取索引前进的距离大于计算得到的距离,就认定该索引已经无效了,因为这就意味拿取索引位于指定索引的后方。这里
    // 后方并不是单纯的指数字更大,由于队列使用的是循环数组的实现,因此这里的后方指的是一下两种情况:
    // · 在相同循环下,拿取索引 > 指定索引;
    // · 拿取索引的循环 > 指定索引的循环。
    // 这两种都可以表示指定索引已经不在当前元素数据的数据段内了,也就是所谓的无效。
    return dequeues > distance;
}

    private void incorporateDequeues() —— 包容出队 —— 判断迭代器的各索引是否依然有效,如果全部失效,则将迭代器分离;否则视情况调整迭代器,令其从原位置或队列头部继续迭代。将迭代器分离前提是游标、下个索引及上个索引全都小于0(特殊情况除外),其分别代表的含义如下:

游标
    NONE(-1):元素数组中已没有可作为(或因为弱一致性不能作为)迭代器下下次迭代的元素;
下个索引
    NONE(-1):元素数组中已没有可作为迭代器下次迭代的元素;
    REMOVED(-2):被选中的作为下次迭代的元素被移除/出队或中途移除;
上个索引
    NONE(-1):迭代器还没有进行迭代或迭代得到的上个索引对应的元素被迭代移除;
    REMOVED(-2):迭代得到的上个索引对应的元素被移除/出队或中途移除。

    上述条件意味着无论元素数组的数据如何变化,都不会再对迭代器造成任何影响,因此可以将迭代器分离。分离后的迭代器会变为分离模式/状态,意味着其与队列断开了关联,不再受队列数据的影响。

/**
 * Adjusts indices to incorporate all dequeues since the last operation on this iterator.  Call only from iterating thread.
 * 在迭代器最后一次操作后调整索引去包容所有的出队(操作)。(该方法)只通过迭代器线程调用。
 *
 * @Description: 包容出队:根据当前元素数组数据段的分布对迭代器的循环及各个索引进行调整。如果调整后发现各个索引都已失效则将迭代器切换
 * @Description: 为分离模式。
 */
private void incorporateDequeues() {
    // assert lock.getHoldCount() == 1;
    // 断言加锁;
    // assert itrs != null;
    // 断言迭代器链不为null;
    // assert !isDetached();
    // 断言当前迭代器不为分离模式;
    // assert count > 0;
    // 断言(队列的元素)总数 > 0;

    final int cycles = itrs.cycles;
    final int takeIndex = ArrayBlockingQueue.this.takeIndex;
    final int prevCycles = this.prevCycles;
    final int prevTakeIndex = this.prevTakeIndex;

    // 如果循环(快照)不等于上个循环(快照),或者拿取索引不等于上个拿取索引,就意味着在迭代器的此次操作与上次之间发生过元素的移除/拿
    // 取操作。通俗的说就是拿取索引发生过移动,并且可能重置了不止一次。
    if (cycles != prevCycles || takeIndex != prevTakeIndex) {
        final int len = items.length;
        // how far takeIndex has advanced since the previous operation of this iterator
        // 自上个迭代器操作后拿取索引前进了多远。

        // 计算自上个迭代器操作后拿取索引前进了多少距离。虽说理论上两次迭代器操作之间拿取索引可以重置无数次,但实际上队列实现对此做了限
        // 制,每一次拿取索引重置时都会判断迭代器链的循环与迭代器的循环是否相差了至少2次,对于这样的迭代器会直接关闭,因此可以知道迭代器
        // 链的循环与迭代器的循环的差值(cycles - prevCycles)只会是0或者1。
        long dequeues = (cycles - prevCycles) * len + (takeIndex - prevTakeIndex);

        // Check indices for invalidation
        // 检查索引的有效性。

        // 检查上个索引的有效性,如果已经失效,则将上个索引赋值为REMOVED(-2)。
        if (invalidated(lastRet, prevTakeIndex, dequeues, len))
            lastRet = REMOVED;
        // 检查下个索引的有效性,如果已经失效,则将下个索引赋值为REMOVED(-2)。
        if (invalidated(nextIndex, prevTakeIndex, dequeues, len))
            nextIndex = REMOVED;
        // 检查游标的有效性,如果已经失效,则将游标赋值为当前索引。之所以将游标设置为当前拿取索引,是因为当前迭代器已不在当前元素数组的
        // 数据段内,需要重新从元素数组的数据段头部,即拿取索引处开始迭代。
        if (invalidated(cursor, prevTakeIndex, dequeues, len))
            cursor = takeIndex;
        if (cursor < 0 && nextIndex < 0 && lastRet < 0)
            // 如果游标、下个索引及上个索引都小于0,这意味着该迭代器可以被分离了。这其中比较难理解的是游标,从上述的代码可知,即使是当前
            // 游标失效,也只会将其赋值为最新的拿取索引。因此游标为负数只有一种情况,即迭代器已经没有更多的元素可迭代了。游标的本质是下下
            // 个元素的索引,如果队列中已经没有了可迭代的下下个元素,或者因为迭代器的弱一致性无法迭代到下下个元素,则就会设置为NONE(-1)。

            // 游标 < 0
            //         NONE(-1):元素数组中已没有可作为(或因为弱一致性不能作为)迭代器下下次迭代的元素(即迭代到了放置索引处);
            // 下个索引 < 0
            //         NONE(-1):元素数组中已没有可作为迭代器下次迭代的元素;
            //         REMOVED(-2):被选中的作为下次迭代的元素被移除/出队或中途移除;
            // 上个索引 < 0
            //         NONE(-1):迭代器还没有进行迭代或迭代得到的上个索引对应的元素被迭代移除;
            //         REMOVED(-2):迭代得到的上个索引对应的元素被移除/出队或中途移除;

            // 之所以要在这三个条件下才能将迭代器切换为分离模式是因为只有这样其才不会受队列数据的影响而发生变化,并且其自身也不会在对队列数
            // 据造成影响。所谓分离,就是与队列在数据上分离开来。
            detach();
        else {
            // 如果当前还有可以迭代的元素,则重新赋值循环与拿取索引。以便于在下一次迭代中进行迭代器是否可用的计算。
            this.prevCycles = cycles;
            this.prevTakeIndex = takeIndex;
        }
    }
}

    private void detach() —— 分离 —— 将迭代器分离,即触发迭代器链的算法清理。所谓的将迭代器分离,具体操作就是将上个拿取索引设置为DETACHED(-3)。

/**
 * Called when itrs should stop tracking this iterator, either because there are no more indices to update (cursor < 0 && nextIndex < 0 && lastRet < 0)
 * or as a special exception, when lastRet >= 0, because hasNext() is about to return false for the first time.  Call only from iterating thread.
 * 当迭代器链应该停止追踪该迭代器时调用,要么因为没有更多的索引可以更新 (cursor < 0 && nextIndex < 0 && lastRet < 0),要么当上个索引 >=0时
 * 作为一个指定例外,因为hasNext()方法将第一次返回false。
 *
 * @Description: 分离:将迭代器切换为分离模式,并触发一次对迭代器链的清理。
 */
private void detach() {
    // Switch to detached mode
    // 切换至分离模式
    // assert lock.getHoldCount() == 1;
    // 断言加锁。
    // assert cursor == NONE;
    // 断言游标为NONE(-1);
    // assert nextIndex < 0;
    // 断言下个索引为负数;
    // assert lastRet < 0 || nextItem == null;
    // 断言上个索引为负数或者下个元素为null;
    // assert lastRet < 0 ^ lastItem != null;
    // 断言上个索引为负数    上个元素不等于null;


    if (prevTakeIndex >= 0) {
        // assert itrs != null;
        // 断言迭代器链不等于null;

        // 将迭代器切换为分离模式。所谓的分离模式可以理解为迭代器与队列彻底分离。即队列的更新不会对迭代器造成影响,反之同样如此。
        prevTakeIndex = DETACHED;

        // 对迭代器链进行清理。
        // try to unlink from itrs (but not too hard)
        itrs.doSomeSweeping(true);
    }
}

    public boolean hasNext() —— 存在下个 —— 判断迭代器是否存在下个元素,存在返回true,否则返回false。由于迭代器会保存元素数组中下次迭代返回元素的快照(即下个元素),因此判断是否还存在下个元素只需直接判断下个元素是否为null即可。由于下个元素本质是一个快照,因为即使元素数组中的数组被移除也不会对迭代器返回该元素造成影响,这就实现了迭代器弱一致性的需求。

    如果下个元素为null,这就意味着迭代器已经迭代完了所有的元素,将第一次返回false。此时会调用noNext()方法,该方法的作用是执行一个特殊操作,即将正常迭代完毕的迭代器分离,即使其不满足索引条件。

/**
 * For performance reasons, we would like not to acquire a lock in hasNext in the common case.  To allow for this, we only access fields (i.e. nextItem)
 * that are not modified by update operations triggered by queue modifications.
 * 因为性能的原因,一般情况下我们不会在hasNext()方法中获取锁。为了可以这么做,我们只访问未被队列修改触发的更新操作修改的字段(即 下个
 * 元素)(下个元素是保存在迭代器中的快照,一旦确定是不会发生变化的)。
 *
 * @Description: 存在下个:判断是否存在下个元素,存在返回true,否则返回false。
 */
public boolean hasNext() {
    // assert lock.getHoldCount() == 0;
    // 断言lock.getHoldCount() == 0(即未加锁状态);

    // 判断下个元素是否存在,如果存在则返回true,否则返回false。
    if (nextItem != null)
        return true;
    // 当下个元素为null时调用noNext()方法。
    noNext();
    return false;
}

    private void noNext() —— 无下个 —— 将迭代器分离,即使群其依然保存着上个索引。该方法的本质是一个特殊操作,目的是将已完全迭代的迭代器强制分离。一般来说,一个刚刚迭代完的迭代器是不满足分离条件的,因为其依然保存着上个索引,这就意味着其如果队列移除(移除/拿取、内部移除及迭代移除)了上个索引在元素数组中对应的元素,则迭代器依然会收到影响(上个索引应该要被赋值为REMOVED(-2))。那为什么要这么做呢?这是因为一个已经迭代完成的迭代器存在的意义已经不大了,其唯一可以执行的操作就是迭代移除,即调用remove()方法移除上个索引在元素数组中对应的元素。但事实是remove()方法并不常用,为了一个未执行的remove()方法或等待其它方式的移除而将一个已经基本无效的迭代器保存在迭代器链中并不划算,因为无法确定移除会在什么时候发生,甚至可能干脆就不发生。如此一来该迭代器就会长期/一直保存在迭代器链中,直至以GC回收的方式从迭代器链中移除。实际上虽然没有具体名言,但代码的种种实现可以看出队列是推荐将一个迭代器分离的。因为这可以减少队列操作时需要操作的迭代器数量,有助于提高性能。因此为了提高性能,队列会将已经迭代器完毕的迭代器强制分离,无论其是否保存着上个索引。

    当然,对于保存着上个索引的迭代器队列也不会放着不管。上文已经说过,在此状态下迭代器唯一能做的就是执行remove()方法,因此只需保证迭代器分离后remove()方法依然可以执行即可。迭代器在分离状态下执行remove()方法最大的问题在其无法确定上个索引在元素数组中对应的元素是否已经移除。正常情况下我们可以通过上个索引是否为NONE(-1)或REMOVED(-2)判断,但由于迭代器已经被分离了,因此元素数组的变化并不会回馈到迭代器上,如此就无法保证remove()方法移除正确性(可能会造成重复移除一个位置的多个元素)。此时,一直没有使用的上个元素就排上了用场,当确定已经没有可迭代的元素时(即hasNext()方法返回false),迭代器会保存上个索引在元素数组中对应元素的快照,即上个元素。当进行移除时,会通过“==”判断上个索引在元素数组中对应的元素是否与上个元素相同,相同则意味着未移除,随后执行移除。

/**
 * @Description: 无下个:
 */
private void noNext() {
    // 加锁。
    final ReentrantLock lock = ArrayBlockingQueue.this.lock;
    lock.lock();
    try {
        // assert cursor == NONE;
        // 断言游标为NONE(-1);
        // assert nextIndex == NONE;
        // 断言下个索引为NONE(-1)(既然没有下个元素,那下个索引自然就是null);

        // 判断当前迭代器是否是分离模式。
        if (!isDetached()) {
            // assert lastRet >= 0;
            // 断言上个索引 >= 0;

            // might update lastRet
            // 可能更新上个索引(如果上个索引已失效,则会被设置为REMOVED(-2))

            // 包括出队。虽然游标及下个索引都为NONE(-1),但迭代器中依然可能保存着上一次迭代的索引。如果此时其已经失败,则此次包容可能会
            // 使其被赋值为REMOVED(-2)。如果真是这样,则还会将迭代器切换为分离模式。
            incorporateDequeues();

            // 如果在包容操作后上个索引 >= 0,意味着上个索引是有效的,且迭代器也没有被切换为分离模式。
            if (lastRet >= 0) {
                // 将上个元素赋值为上个索引在元素数组中对应的元素。一般情况下迭代器只会将上个索引赋值为下个索引,但不会将上个元素赋值为下个元
                // 素(该操作在next()方法中发生)。这是因为下个元素本身是个快照,是不准确的,未必真的是下个元素在元素数组中对应的元素,只被用
                // 来判断和返回。而在此处之所以要在hasNext()方法返回false时将上个元素赋值为上个索引在元素数组中对应的元素,是为"将已确定迭代完
                // 毕的迭代器切换为分离状态并将之从迭代器链中移除"这步特殊操作所作出的兼容。由于当前迭代器会被强制切换为分离模式并从迭代器中移,
                // 这就意味着后续队列的所有数据变更都不会再对当前的迭代器造成影响。这就意味着即使队列已经将上个索引在元素数据中队列的元素删除,
                // 迭代器的上个索引也不会被赋值为REMOVED(-2)。如此依赖会导致迭代移除remove()方法收到影响,简单的说就是不知道迭代器中保存
                // 的索引值(假设有效)在元素数组中对应的元素是否已被移除。为了兼容这一点,做法是将对应的元素保存在迭代器的上个元素中。当执行
                // 迭代移除remove()方法时会新增对元素值的判断,以确保其确实是相同元素。
                lastItem = itemAt(lastRet);
                // assert lastItem != null;
                // 断言上个元素不为null;

                // 如果在确定没有后续迭代元素,并且包容出队后上个索引依然有效,则强制性的将迭代器切换为分离模式。这是一步特殊操作,打破了将迭代
                // 器切换为分离模式的前提条件(游标/下个索引/上个索引 < 0)。之所以这么做是因为当前迭代器已经遍历完了所有可遍历的元素,唯一阻止
                // 其切换为分离状态的条件就是其依然保存着上个元素。实际上虽然没有具体名言,但代码的种种实现可以看出队列是推荐将一个迭代器切换为
                // 分离模式的。因为这可以减少队列操作时需要操作的迭代器数量,有助于提高性能。而对于当前状态的迭代器,由于其依然保存着上个索引的
                // 值,因此除非迭代移除,否则是无法被切换为分离模式的。但是迭代移除本身也并不是常用操作,如此一类该迭代器大概率就只能通过自身被
                // GC回收的方式从迭代器链移除,并且这还需要其它触发对迭代器链的清理。因此基于性能的考虑,会已完成迭代的迭代器直接强制切换为分离
                // 模式并将之从迭代器链中移除,无论其是否保存着上个索引。当然,对于保存着上个索引的迭代器也不会放着不管,上文中已经铲除了该情况
                // 下具体的操作。
                detach();
            }
        }
        // assert isDetached();
        // assert lastRet < 0 ^ lastItem != null;
    } finally {
        lock.unlock();
    }
}

    public E next() —— 下个 —— 返回下个迭代的元素。由于迭代器中保存下个元素,因此直接将该元素返回即可。由于下个元素已经被返回,因此要将上个索引赋值为下个索引。于此同时,还需要做的一件事是重新定位下个迭代的元素。之前已经说过,游标中保存着迭代器下下个迭代元素的索引。因此我们可直接将下个索引赋值为游标,并通过新下个索引自元素数组中获取对应元素保存在下个元素中,随后递增游标,定位下下个游标。如果在定位中发现已经没有新的元素可以迭代,则将游标赋值为NONE(-1)。

/**
 * @Description: 下个:返回保存在迭代器中的下个元素,并查找下个需要返回的元素。
 */
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;
        // 断言下个索引不为NONE(-1);
        // assert lastItem == null;
        // 断言上个元素为null;

        // 将上个索引赋值为下个索引,但不对上个元素赋值。
        lastRet = nextIndex;
        final int cursor = this.cursor;
        if (cursor >= 0) {
            // 如果游标大于等于0,意味着队列中还存在元素可迭代。将下个索引赋值为游标,并通过下个索引获取到元素,将之设置为下个元素。
            nextItem = itemAt(nextIndex = cursor);
            // assert nextItem != null;
            // 递增游标,如果递增后的游标等于放置索引,则说明已经没有后续元素,将之赋值为NONE(-1)。
            this.cursor = incCursor(cursor);
        } else {
            // 如果游标小于0,则说明已经没有可以迭代的元素了,或者即使存在(可能在判断没有之后又新增了),也因为弱一致性的原因不需要
            // 去访问了。如此就直接将下个元素赋值为NONE(-1),而下个元素则赋值为null。
            nextIndex = NONE;
            nextItem = null;
        }
    } finally {
        lock.unlock();
    }
    return x;
}

    public void remove() —— 移除 —— 移除上个迭代的元素。从元素数组中移除上个迭代的元素是通过上个索引完成的,如果上个索引是正常值,则可知直接通过其自元素数组中将元素移除,元素移除后,上个元素会被赋值为NONE(-1),而不是REMOVED(-2)。如果移除的是一个内部元素,则会导致元素的迁移,这会进一步引发队列对迭代器链的遍历,目的是针对元素迁移对所有的为分离迭代器进行调整,并顺势清理所有的无效节点。

    该方法有一个特殊情况,即上文中已经提及的因为已正常完成迭代而被强制分离的迭代器执行remove()方法的情况。迭代器在分离状态下执行remove()方法最大的问题在其无法确定上个索引在元素数组中对应的元素是否已经移除。正常情况下我们可以通过上个索引是否为NONE(-1)或REMOVED(-2)判断,但由于迭代器已经被分离了,因此元素数组的变化并不会回馈到迭代器上,如此就无法保证remove()方法移除正确性(可能会造成重复移除一个位置的多个元素)。此时,一直没有使用的上个元素就排上了用场,当确定已经没有可迭代的元素时(即hasNext()方法返回false),迭代器会保存上个索引在元素数组中对应元素的快照,即上个元素。当进行移除时,会通过“==”判断上个索引在元素数组中对应的元素是否与上个元素相同,相同则意味着未移除,随后执行移除。

/**
 * @Description: 移除:移除当前迭代得到的元素,即上个元素。
 */
public void remove() {
    // assert lock.getHoldCount() == 0;
    // 断言未加锁;

    // 加锁。
    final ReentrantLock lock = ArrayBlockingQueue.this.lock;
    lock.lock();
    try {
        // 判断迭代器是否是分离模式。
        if (!isDetached())
            // might update lastRet or detach
            // 可能更新上个索引或分离。

            // 包容出队。如果上个索引已失效,则会在该操作中被赋值为REMOVED(-2)。
            incorporateDequeues();

        // 将上个索引赋值为NONE(-1),表示不存在上个元素。这个因为该元素会在当前方法中被移除。
        final int lastRet = this.lastRet;
        this.lastRet = NONE;
        if (lastRet >= 0) {
            // 如果上个索引(快照) >= 0,意味着上个索引是有效的。
            if (!isDetached())
                // 迭代器不是分离模式,可直接将从元素数组中上个索引所指的元素移除。
                removeAt(lastRet);
            else {
                // 如果上个索引(快照) >= 0,但迭代器是分离模式,那就只有一种可能,即迭代器已经遍历完了所有的元素,并调用hasNext()方
                // 法返回false。在这种情况下,上个元素是存有值的。由于我们无法通过上个索引来判断其对应的元素是在已经元素数组中被移除,
                // 因此只能转而通过比对元素是否相同的方式进行比对。
                final E lastItem = this.lastItem;
                // assert lastItem != null;
                this.lastItem = null;

                // 如果上个索引在元素数组中对应的值与上个元素相同,则意味着在该元素依然保存在元素数据中,迭代移除。
                if (itemAt(lastRet) == lastItem)
                    removeAt(lastRet);
            }
        } else if (lastRet == NONE)
            // 如果上个索引(快照) == NONE(-1),则要么说明迭代器上尚未进行任何一次迭代;要么说明迭代的元素已被迭代移除。这两种情况
            // 都不支持重复移除,因此直接抛出非法操作异常。
            throw new IllegalStateException();

        // else lastRet == REMOVED and the last returned element was previously asynchronously removed via an operation other than
        // this.remove(), so nothing to do.
        // 对于lastRet == REMOVED和最后返回的元素是通过不同于this.remove()(迭代移除)方法的操作  异步移除,所以什么也不做。

        if (cursor < 0 && nextIndex < 0)
            detach();
    } finally {
        lock.unlock();
        // assert lastRet == NONE;
        // assert lastItem == null;
    }
}

    void shutdown() —— 关闭 —— 强制分离迭代器。该方法会在迭代器长期未使用时调用,强制将迭代器的所有索引赋值为特殊值,并将之分离,以防止迭代器的弱一致性过于弱。

/**
 * Called to notify the iterator that the queue is empty, or that it has fallen hopelessly behind, so that it should abandon any further iteration,
 * except possibly to return one more element from next(), as promised by returning true from hasNext().
 * 调用(该方法)通知迭代器队列已空,或者它已经无可避免地落后于后面,所以其应该放弃任何进一步的迭代,除非next()方法能返回一个或
 * 更多元素,作为hasNext()方法返回true的保证。
 *
 * @Description: 关闭:关闭迭代器,意味着所有索引值的都会被赋值于为特殊值,并被切换为分离模式。
 */
void shutdown() {
    // assert lock.getHoldCount() == 1;
    // 断言加锁。

    // 将游标赋值为NONE(-1),表示已经没有新的可迭代的元素。
    cursor = NONE;
    if (nextIndex >= 0)
        // 如果下个索引 >= 0,则将之赋值为REMOVED(-1)。
        nextIndex = REMOVED;
    if (lastRet >= 0) {
        // 如果上个索引 >= 0,则将之赋值为REMOVED(-1),并将下个元素赋值为null。
        lastRet = REMOVED;
        lastItem = null;
    }
    // 将迭代器切换为分离模式。
    prevTakeIndex = DETACHED;
    // Don't set nextItem to null because we must continue to be able to return it on next().
    // 不要将下个元素赋值为null,因为我们其必须能够继续在next()方法中返回。
    // Caller will unlink from itrs when convenient.
    // 当方便时调用者应将迭代器从迭代器链中移除。
}

    private int distance(int index, int prevTakeIndex, int length) —— 距离 —— 用于获取上个拿取索引到指定索引之间的距离。

/**
 * @Description: 距离:用于获取上个拿取索引到指定索引之间的距离。
 */
private int distance(int index, int prevTakeIndex, int length) {
    int distance = index - prevTakeIndex;
    if (distance < 0)
        distance += length;
    return distance;
}

    boolean removedAt(int removedIndex) —— 移除所在 —— 根据移除索引对迭代器的索引进行调整。该方法会在队列发生内部移除时调用,主要是根据移除发生的位置,对迭代器的各个索引进行调整,以确保迭代的正常执行,具体操作如下:

游标
    游标等于移除索引 && 游标等于放置索引:意味着迭代器已经没有迭代的下下个元素,> 将游标赋值为NONE(-1);
    游标位于移除索引之后:将游标递减以配合元素的迁移;


下个索引
    下个索引等于移除索引:意味着下个索引在元素数组中对应的元素已被移除,将下个索引赋值为REMOVED(-2);
    下个索引位于移除索引之后:将下个索引递减以配合元素的迁移;


上个索引
    上个索引等于移除索引:意味着上个索引在元素数组中对应的元素已被移除,将上个索引赋值为REMOVED(-2);
    上个索引位于移除索引之后:将上个索引递减以配合元素的迁移;

    如果调整后发生所有的索引都以小于0,则意味着迭代器已经无法再迭代器了,从而将之分离。

/**
 * Called whenever an interior remove (not at takeIndex) occurred.
 * 当迭代器发生移除(不在拿取索引位置)时调用。
 *
 * @return true if this iterator should be unlinked from itrs 大部分迭代器应该从迭代器链中断开时返回true
 * @Description: 移除所在:根据移除索引对迭代器的索引进行调整。
 */
boolean removedAt(int removedIndex) {
    // assert lock.getHoldCount() == 1;
    // 断言加锁。

    // 如果迭代器已是分离模式,则直接返回true。
    if (isDetached())
        return true;

    final int cycles = itrs.cycles;
    final int takeIndex = ArrayBlockingQueue.this.takeIndex;
    final int prevCycles = this.prevCycles;
    final int prevTakeIndex = this.prevTakeIndex;
    final int len = items.length;

    // 计算循环差,即迭代器链的循环与迭代器的上个循环的差,用于计算移除距离。
    int cycleDiff = cycles - prevCycles;
    if (removedIndex < takeIndex)
        // 传入的移除索引必然是合法的,比拿取索引还小就意味着移除索引的循环比拿取索引的循环还要大上一轮,因此循环差要递增一次。
        cycleDiff++;
    // 计算移除距离,即上个拿取索引与移除索引之间的距离。
    final int removedDistance = (cycleDiff * len) + (removedIndex - prevTakeIndex);
    // assert removedDistance >= 0;
    // 断言移除距离 >= 0;

    // 重定位游标。
    int cursor = this.cursor;
    // 如果游标 >=0,意味着迭代器除下个元素外还存在可迭代的元素,即使对比当前的元素数组的数据段其已经失效,其也可以从拿取索引
    // 的位置继续遍历。
    if (cursor >= 0) {
        // 计算游标距离,即上个拿取索引与游标之间的距离。
        int x = distance(cursor, prevTakeIndex, len);
        if (x == removedDistance) {
            // 如果游标距离 = 移除距离,并且游标等于放置索引,则说明迭代器除了下个元素以外就没有了可迭代的元素了,直接将索引赋值为
            // NONE(-1)。
            if (cursor == putIndex)
                this.cursor = cursor = NONE;
        } else if (x > removedDistance) {
            // assert cursor != prevTakeIndex;
            // 断言游标 != 上个拿取索引(索引 = 上个放置索引是一种极短暂的状态,即在包容出队之后如果索引失效,则其可能会使其等于放置
            // 索引。但在这种情况下,迭代器要么被切换为分离模式(分离模式的迭代器不会执行该方法);要么在包容出队后续的操作中被转
            // 化NONE(-1)或者非放置索引,这也是为何这里断言游标 != 上个拿取索引的原因);

            // 如果游标距离 > 移除距离,意味着移除索引处于游标之前(移除索引是合法的,则必然位于拿取索引之后,则游标也必然位于拿取
            // 索引之后,也就是对于当前元素数组的数据段,游标是有效的),并且由于是中途移除,故而后续的元素会向前迁移,因此游标应
            // 该向前迁移一位,以对应之前的元素,故递减游标。
            this.cursor = cursor = dec(cursor);
        }
        // 如果游标距离 < 移除距离,意味着移除索引位于游标之后。这种情况下,无论索引是否有效,移除都不会对其造成影响。因此也就无
        // 需操作。下面的上/下个索引也是一样的道理。
    }

    // 重定位上个索引。
    int lastRet = this.lastRet;
    // 如果上个索引 >= 0,则意味着迭代器进行过迭代。但是由于当前没有执行前和执行过程中都没有进行包容出队操作,因此对于当前元素
    // 数组的数据段,其可能是无效的。
    if (lastRet >= 0) {
        // 计算上个索引距离,即上个拿取索引与上个索引之间的距离。
        int x = distance(lastRet, prevTakeIndex, len);
        if (x == removedDistance)
            // 如果上个索引距离 = 移除距离,意味着移除的元素刚好就是迭代器的上个元素。因此应该将上个元素赋值为特殊值。而由于不是通过
            // 当前迭代器的迭代移除的,因此需要赋值为REMOVED(-2)。
            this.lastRet = lastRet = REMOVED;
        else if (x > removedDistance)
            // 如果上个索引距离 > 移除距离,意味着上个索引位于移除索引之后(因此上个索引是有效的)。如此一来就会造成移除索引之后的元
            // 素向前 迁移一位,因此上个索引也需要迁移一位,来和原来的元素匹配。
            this.lastRet = lastRet = dec(lastRet);
    }

    // 重定位下个索引。
    int nextIndex = this.nextIndex;
    if (nextIndex >= 0) {
        // 如果下个索引 >= 0,意味着当前元素存在可迭代的下个元素。但是由于当前没有执行前和执行过程中都没有进行包容出队操作,因此对
        // 于当前元素数组的数据段,其可能是无效的。
        // 计算下个索引距离,即上个拿取索引与下个索引之间的距离。
        int x = distance(nextIndex, prevTakeIndex, len);
        if (x == removedDistance)
            // 如果下个索引距离 = 移除距离,可知移除的元素就是保存在迭代器中的下个索引对应的元素。但这不会影响迭代器下次的正常迭代,因
            // 为在迭代器中保存了该元素的快照,也就是下个元素。由于元素已被移除,因此迭代器中的下个索引应该被设置为特殊值。由于是移除,
            // 因此被赋值为赋值为REMOVED(-2)。也就是说,迭代的下个索引与下个元素并不会出现不匹配的情况,如果下个索引在元素数组上对
            //对应的元素被移除,则其会赋值为REMOVED(-2),而不是被赋值为游标。
            this.nextIndex = nextIndex = REMOVED;
        else if (x > removedDistance)
            // 如果下个索引距离 > 移除距离,意味着下个索引位于移除索引之后(因此下个索引是有效的)。如此一来就会造成移除索引之后的元素
            // 向前 迁移一位,因此下个索引也需要迁移一位,来和原来的元素匹配。
            this.nextIndex = nextIndex = dec(nextIndex);
    } else if (cursor < 0 && nextIndex < 0 && lastRet < 0) {
        // 游标/下个索引/上个索引 < 0,则将当前迭代器切换为分离模式。在该方法这确实是可能的,例如:
        // 游标为NONE(-1)(已没有元素可作为下个元素),下个索引为NONE(-1)(游标为NONE(-1)),上个索引为正常索引值;
        // 游标为NONE(-1)(已没有元素可作为下个元素),下个索引为REMOVED(-2)(被移除),上个索引为正常索引值;
        // 这两种场景都会在迭代器在执行了上述逻辑后被切换为分离模式。
        this.prevTakeIndex = DETACHED;
        return true;
    }
    return false;
}

    boolean takeIndexWrapped() —— 拿取索引包装 —— 当拿取索引重置时调用,判断当前迭代器是否应该从迭代器链中移除,应该则返回true;否则返回false。该方法的具体作用是判断迭代器使用已经长期未使用,如果尝试未使用,则将至强制关闭,这是队列为了防止迭代器的弱一致性过于弱的一种方式。如果判断迭代器是否已经长期未使用呢?具体的方法是判断当前迭代器链的循环以迭代器的上个循环的差值是否大于1。如果大于1,就该迭代器已经长时间未使用了。因为这意味着在距离迭代器上一次操作的时间里,拿取索引至少已经经历了两次重置。这代表在迭代器上一次操作时存在于队列中的元素已经全部被移除了(差值为0意味着元素必然有所保留;差值为1意味着可能有所保留,也可能全部移除;差值为2则必然已经全部移除),即使元素数据中还存在着其它元素,也是后期添加的。在这种情况下,保存在迭代器中的下个元素的弱一致性就有些太弱了,因此直接将该迭代器关闭,使其不再进行迭代。

/**
 * Called whenever takeIndex wraps around to zero.
 * 当拿取索引环绕0(即重置)时调用。
 *
 * @return true if this iterator should be unlinked from itrs 如果该迭代器应该从迭代器链中移除则返回true。
 * @Description: 拿取索引包装:当拿取索引重置时调用,判断当前迭代器是否应该从迭代器链中移除,应该则返回true;否则返回false。
 */
boolean takeIndexWrapped() {
    // assert lock.getHoldCount() == 1;
    // 断言加锁。

    // 判断当前迭代器是否是分离,如果是则返回true。
    if (isDetached())
        return true;
    // 判断迭代器链中保存的循环 - 迭代器中保存的上个循环是否大于1。如果是,就说明在迭代器生成后拿取索引至少经历了2次重置。
    if (itrs.cycles - prevCycles > 1) {
        // All the elements that existed at the time of the last operation are gone, so abandon further iteration.
        // 在最后一次操作时存在的所有元素都消失了,因此放弃进一步迭代(意思是当前迭代器最后一次迭代的时候存在于队列中的那些元素
        // 都已经消失了,因此也就无需在继续迭代了...)。

        // 如果对于当前迭代器来说,如果与迭代器链的循环相差了至少2,意味着其原本所在数据段的数据已被全部清除。即使当前元素数组中
        // 有着其他数据,也是后继添加的。对于这种情况,采用的做法是直接将之关闭,即将所有的索引都设置为特殊值,并将迭代器切换为分
        // 离模式。
        // 由此可以知道,虽然迭代器可以包容出队,即即使迭代器已经不处于当前元素数组的数据段中了,但只要元素数据还存在元素,就依然
        // 可以从头开始迭代...但这个不是没有限制的,如果两次迭代器操作期间拿取索引的重置次数达到了至少两次,则不会再进行重置,而是
        // 直接迭代器关闭。
        shutdown();
        return true;
    }
    return false;
}

五 相关系列


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

说淑人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值