ArrayBlockingQueue 学习

作用

有界阻塞队列, 既然是阻塞说明用到了锁,所以本质上各种方法是只能串行,所以该类是线程安全的,比较简单。

实现原理

1、加锁只要是对共享队列的操作,ArrayBlockingQueue使用锁保证线程安全
2、有界数组队列当作,循环队列使用
3、如果队列满了, 入队的线程将会进行阻塞在notFull条件队列上
4、如果队列空了,出队的线程将会阻塞在notEmpty条件队列上
5、添加了元素,将会唤醒notEmpty上的线程
6、元素出队将会唤醒notFull上的线程。

字段属性

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.
     */
    private static final long serialVersionUID = -817911632652898426L;

    /** The queued items */
    final Object[] items; //数组队列

    /** items index for next take, poll, peek or remove */
    int takeIndex; //下一个出队的索引

    /** items index for next put, offer, or add */
    int putIndex; //下一个入队的索引

    /** Number of elements in the queue */
    int count; //元素个数

    /*
     * Concurrency control uses the classic two-condition algorithm
     * found in any textbook.
     */

    /** Main lock guarding all access */
    final ReentrantLock lock; //锁

    /** Condition for waiting takes */
    private final Condition notEmpty; //空了

    /** Condition for waiting puts */
    private final Condition notFull; //满了

    /**
     * Shared state for currently active iterators, or null if there
     * are known not to be any.  Allows queue operations to update
     * iterator state.
     */
    transient Itrs itrs = null; //迭代器
 }

构造方法

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) {
        this(capacity, fair); //队列元素进行初始化

        final ReentrantLock lock = this.lock; //final 保证了可见性,而且不能修改引用
        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) {
                throw new IllegalArgumentException();
            }
            count = i;
            putIndex = (i == capacity) ? 0 : i;//注意 队列是逻辑头尾环形队列处理 putIdex指下一个入队的位置
        } finally {
            lock.unlock();
        }
    }

offer

public boolean offer(E e) { //从队尾添加一个
        checkNotNull(e); //检查不等于空
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            if (count == items.length) //说明队列满了
                return false;
            else {
                enqueue(e); //入队
                return true;
            }
        } finally {
            lock.unlock();
        }
    }

enqueue

private void enqueue(E x) {
        // assert lock.getHoldCount() == 1;
        // assert items[putIndex] == null;
        final Object[] items = this.items;
        items[putIndex] = x; //入队
        if (++putIndex == items.length) //是否达到环形开始条件
            putIndex = 0;
        count++; //个数加一
        notEmpty.signal(); //唤醒一个等待队列里面的线程
    }

poll

public E poll() {
        final ReentrantLock lock = this.lock; //获取锁
        lock.lock(); //加锁
        try {
            return (count == 0) ? null : dequeue(); //是否有元素可以出队
        } finally {
            lock.unlock(); //解锁
        }
    }

dequeue

private E dequeue() {
        // assert lock.getHoldCount() == 1;
        // assert items[takeIndex] != null;
        final Object[] items = this.items;
        @SuppressWarnings("unchecked")
        E x = (E) items[takeIndex];
        items[takeIndex] = null;
        if (++takeIndex == items.length) //出队回到起点?
            takeIndex = 0;
        count--; //数量减少
        if (itrs != null) //迭代器有关
            itrs.elementDequeued();
        notFull.signal(); //出队之后唤醒等待队列里面的一个, 有位置可以进行入队了
        return x;
    }

take

public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly(); //阻塞的时候可以响应中断
        try {
            while (count == 0) //没有出队的
                notEmpty.await(); //阻塞
            return dequeue(); //出队
        } finally {
            lock.unlock();
        }
    }

remove

public boolean remove(Object o) {
        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])) { //判断是否相等
                        removeAt(i); //移除
                        return true;
                    }
                    if (++i == items.length) //到了循环位置
                        i = 0;
                } while (i != putIndex);
            }
            return false;
        } finally {
            lock.unlock();
        }
    }

removeAt

void removeAt(final int removeIndex) {
        // assert lock.getHoldCount() == 1;
        // assert items[removeIndex] != null;
        // assert removeIndex >= 0 && removeIndex < items.length;
        final Object[] items = this.items;
        if (removeIndex == takeIndex) { //说明是刚刚好在出队位置, 直接当作一次peek就行
            // 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;
                if (next == items.length)
                    next = 0;
                if (next != putIndex) { //不到末尾进行覆盖
                    items[i] = items[next];
                    i = next;
                } else { //到了末尾进行置空
                    items[i] = null;
                    this.putIndex = i;
                    break;
                }
            }
            count--;
            if (itrs != null)
                itrs.removedAt(removeIndex);
        }
        notFull.signal(); //唤醒
    }

迭代器功能

直接翻译注释了, 迭代器整体的思想还是挺难的。

迭代器及其队列之间的共享数据,允许队列修改在删除元素时更新迭代器。为了正确处理一些不常见的操作,这增加了很多复杂性,但圆形数组和支持内部删除(即那些不在头部的)的组合会导致迭代器有时丢失它们的位置和/或(重新)报告它们不应该报告的元素。为了避免这种情况,当一个队列有一个或多个迭代器时,它通过以下方式保持迭代器状态的一致性:(1)跟踪“循环”的数量,即takeIndex被包装到0的次数。(2)当一个内部元素被移除时,通过回调函数removedAt通知所有的迭代器(因此其他元素可能会被移动)。这些足以消除迭代器的不一致性,但不幸的是增加了维护迭代器列表的次要责任。我们跟踪一个简单链表(仅在队列锁被持有时访问)中对Itr的弱引用的所有活动迭代器。使用3种不同的机制来清理列表:(1)每当创建新的迭代器时,执行O(1)检查过时的列表元素。(2)当takeIndex绕到0时,检查是否有超过一个循环周期未使用的迭代器。(3)当队列变为空的时候,所有的迭代器都被通知,整个数据结构被丢弃。因此,除了确保正确性所必需的removedAt回调外,迭代器还有shutdown和takeIndexWrapped回调,可以帮助从列表中删除过时的迭代器。每当检查一个列表元素时,如果GC确定迭代器被丢弃,或者迭代器报告它已“分离”(不需要任何进一步的状态更新),它就会被删除。当takeIndex从不前进,迭代器在耗尽之前被丢弃,并且所有删除都是内部删除时,开销是最大的,在这种情况下,所有过时的迭代器都会被GC发现。但即使在这种情况下我们也不会增加平摊复杂度。必须注意防止列表清除方法重入调用另一个这样的方法,从而导致微妙的损坏bug。

多读几遍多想。

迭代器表???

class Itrs {

        /**
         * Node in a linked list of weak iterator references.
         */
        private class Node extends WeakReference<Itr> { //弱引用队列, 即迭代器表的节点
            Node next; //指向下一个迭代器节点

            Node(Itr iterator, Node next) {
                super(iterator); //当前Node节点的迭代器
                this.next = next; //指向下一个迭代器
            }
        }

        /** Incremented whenever takeIndex wraps around to 0 */
        int cycles = 0; //takeIndex为0 , cycles进行加一

        /** Linked list of weak iterator references */
        private Node head; //指向迭代器链表的头指针

        /** Used to expunge stale iterators */
        private Node sweeper = null; //下次开始进行遍历进行迭代器清理的位置, 这里是为了避免进行全局遍历的优化,比如有1000个迭代器,如果遍历了20个迭代器还是没有发现已被GC回收的线程,那么就结束本次遍历,如果发现一个那么遍历次数为0,继续20个接着遍历

        private static final int SHORT_SWEEP_PROBES = 4; //长度为4的遍历长度
        private static final int LONG_SWEEP_PROBES = 16; //长度为16的遍历长度
 }

这里面的知识点除了弱引用, 需要考虑清楚cycles和sweeper的作用是为了什么,对于cycles我的理解就是记录当前队列出队从新经过对头的次数, 应为迭代器是共享的同一个队列,如果别的线程一直在poll, 这样会导致元素原本的一轮上的节点全不被出队掉了, 所以迭代器也就没有遍历的必要

Itrs构造方法

        Itrs(Itr initial) {
            register(initial); //进行注册迭代器到链表中, 即头节点的初始化
        }
        
		void register(Itr itr) {
            // assert lock.getHoldCount() == 1;
            head = new Node(itr, head); //头插入法
        }

迭代器对象

还是直接翻译注释吧, 大佬设计思想:

迭代器之间。为了保持放取操作的弱一致性,我们提前读取一个槽位,这样就不会报告hasNext为真,但也没有要返回的元素。当所有索引都为负数,或者hasNext第一次返回false时,我们切换到“分离”模式(允许在没有GC帮助的情况下从itrs提示断开链接)。这允许迭代器完全准确地跟踪并发更新,除了用户在hasNext()返回false后调用iterator .remove()的特殊情况。即使在这种情况下,我们也可以通过跟踪lasttitem中要删除的元素来确保不会删除错误的元素。是的,我们可能无法从队列中移除lasttem,如果它在分离模式下因为交错的内部移除而移动。

数据结构

private class Itr implements Iterator<E> {
        /** Index to look for new nextItem; NONE at end */
        private int cursor; //指向新的nextItem

        /** Element to be returned by next call to next(); null if none */
        private E nextItem; //避免内部节点删除,迭代器hashNext的下一个节点

        /** 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; //追后遍历到的索引下标

        /** Previous value of takeIndex, or DETACHED when detached */
        private int prevTakeIndex; //上一次进行take的位置记录

        /** Previous value of iters.cycles */
        private int prevCycles;//记录循环从头开始遍历的次数

        /** Special index value indicating "not available" or "undefined" */
        private static final int NONE = -1; //没有值

        /**
         * Special index value indicating "removed elsewhere", that is,
         * removed by some operation other than a call to this.remove().
         */
        private static final int REMOVED = -2; //被移除

        /** Special value for prevTakeIndex indicating "detached mode" */
        private static final int DETACHED = -3; //被分离

迭代器构造方法

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 {
                    final int takeIndex = ArrayBlockingQueue.this.takeIndex; //获取出队位置
                    prevTakeIndex = takeIndex;
                    nextItem = itemAt(nextIndex = takeIndex); //获取下一个出队的元素
                    cursor = incCursor(takeIndex); //指向最新的nextItem索引
                    if (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();
            }
        }

迭代器的初始化的时候进行了一个迭代器表的初始化

doSomeSweeping

扫描 itrs,寻找并删除过时的迭代器。如果至少找到一个,请努力寻找更多。仅从迭代线程调用。

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

            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; //获取p的后继
                if (it == null || it.isDetached()) { //如果改迭代器已经被垃圾回收 或者 是被分离了
                    // found a discarded/exhausted iterator
                    probes = LONG_SWEEP_PROBES; // "try harder"  既然有一个进行了清理, 说明更多的,所以探针范围扩大
                    // unlink p
                    p.clear();//清楚p的迭代器
                    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; //继续往后, 没有过时的迭代器, o 往后走
                }
                p = next; //p往后走
            }

            this.sweeper = (p == null) ? null : o; //记录扫描位置

hasNext

出于性能原因,我们不希望在常见情况下获取 hasNext 中的锁。为此,我们只访问未被队列修改触发的更新操作修改的字段(即 nextItem)

public boolean hasNext() {
            // assert lock.getHoldCount() == 0;
            if (nextItem != null) //下一项不为空
                return true;
            noNext(); //说明迭代器已经到底了, 需要进行分离
            return false;
        }

next

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; //lastRet 下标进行替换
                final int cursor = this.cursor;// 
                if (cursor >= 0) { //还有最新下标元素可以进行遍历
                    nextItem = itemAt(nextIndex = cursor);
                    // assert nextItem != null;
                    this.cursor = incCursor(cursor); //获取最新的nextItem下标
                } else {//说明迭代器已经遍历到头了
                    nextIndex = NONE;
                    nextItem = null;
                }
            } finally {
                lock.unlock();
            }
            return x;
        }

elementDequeued

每当元素出队时调用(在 takeIndex 处)。

void elementDequeued() {
            // assert lock.getHoldCount() == 1;
            if (count == 0) //队列元素为0
                queueIsEmpty(); //清空迭代器表
            else if (takeIndex == 0) //出队循环到从头开始
                takeIndexWrapped(); //移除没有跟上的迭代器迭代器
        }

queueIsEmpty

每当队列变空时调用。通知所有活动的迭代器队列为空,清除所有弱引用,并取消链接 itrs 数据结构。

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; //清除
        }

shutdown

调用来通知迭代器队列是空的,或者它已经无可救药地落后了,因此它应该放弃任何进一步的迭代,除了可能从 next() 返回一个元素,正如从 hasNext() 返回 true 所承诺的那样。

void shutdown() {
            // assert lock.getHoldCount() == 1;
            cursor = NONE; //最新的nextItem为空
            if (nextIndex >= 0)
                nextIndex = REMOVED; //移除
            if (lastRet >= 0) {
                lastRet = REMOVED; //移除
                lastItem = null;
            }
            prevTakeIndex = DETACHED; //分离
            // Don't set nextItem to null because we must continue to be
            // able to return it on next().
            //
            // Caller will unlink from itrs when convenient.
        }

不要将 nextItem 设置为 null,因为我们必须继续能够在 next() 上返回它。来电者将在方便时取消与 itrs 的链接。

takeIndexWrapped

每当 takeIndex 回绕到 0 时调用。通知所有迭代器,并删除任何现在过时的迭代器。

void takeIndexWrapped() { //当takeIndex为0, 进行迭代器清理工作
            // assert lock.getHoldCount() == 1;
            cycles++; //次数加一
            for (Node o = null, p = head; p != null;) {//o 记录为p的头指针, 开始进行遍历
                final Itr it = p.get(); //获取迭代器
                final Node next = p.next; //获取p节点的后继
                if (it == null || it.takeIndexWrapped()) { //当前迭代器被GC回收了或者当前迭代器是否是需要被回收(即跟步上)
                    // unlink p
                    // assert it == null || it.isDetached();
                    p.clear(); //置空
                    p.next = null; //从迭代器链表进行脱离
                    if (o == null) //说明这个迭代器节点是头节点
                        head = next; //头指针指向next
                    else
                        o.next = next; //跨过p节点
                } else { //p不需要进行清理
                    o = p; //o指向p
                }
                p = next; //推进
            }
            if (head == null)   // no more iterators to track 判断迭代器表里已经没有迭代器了
                itrs = null; //进行空间回收
        }

takeIndexWrapped

boolean takeIndexWrapped() { //判断当前迭代器是否是需要被回收
            // assert lock.getHoldCount() == 1;
            if (isDetached()) //如果迭代器被分离了
                return true;
            if (itrs.cycles - prevCycles > 1) { //当前迭代器是否没有被甩了一圈, 哈哈哈,这个形容词比较好。
                // All the elements that existed at the time of the last
                // operation are gone, so abandon further iteration. 次操作时存在的所有元素都消失了,因此放弃进一步的迭代。
                shutdown(); //迭代器进行关闭
                return true;
            }
            return false;
        }

removedAt (itrs)

每当发生内部删除(不在 takeIndex 处)时调用。

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;
        }

removedAt(Itr)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值