欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

源码剖析之ArrayBlockingQueue

程序员文章站 2022-04-21 09:26:37
...
ArrayBlockingQueue 是jdk1.5 新提供的阻塞队列,实现了固定大小的队列。

功能:
1、阻塞的效果 。put时如果元素已经满,那么阻塞,get时 如果队列为空,那么阻塞。
2、是实现生产者消费者模型的极好的备选工具。

实现依赖:
1、lock锁(内存的可见性、互斥访问、限制编译器的代码优化调整)
2、Condition条件通知(线程间的协作)


注意点:代码中多次用的signal 而不是signalAll 有原因的:
1、signalAll 唤醒所有等待的线程,事实上只能有一个通过获得锁,那么会增加锁竞争的几率。效率也低,如果用signal ,那么仅唤醒一个线程,这正是我们所需要的场景!


为了看清楚ArrayBlockingQueue 的工作原理,请参看其源代码的分析如下:
package java.util.concurrent;
import java.util.concurrent.locks.*;
import java.util.*;


public class ArrayBlockingQueue<E> extends AbstractQueue<E>
        implements BlockingQueue<E>, java.io.Serializable {

    
    private static final long serialVersionUID = -817911632652898426L;

    /** 队列的项,底层是数组,final 修饰意味着 ArrayBlockingQueue 是固定大小的队列 */
    private final E[] items;
    /** 对数组items 下一个 take, poll or remove 操作的索引index */
    private int takeIndex;
    /** 对数组items 下一个 put, offer, or add 操作的索引index */
    private int putIndex;
    /**队列中数据项的数量 ,必须在lock当前锁的情况下才可以使用
     注意:count 不需要volitile 修饰, tackIndex putIndex 都一样,因为使用它们的场景都有lock 锁,所以内存可见性和编译代码调整的优化 得到安全的保障!!
     */
    private int count;

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

    /**  数据访问的锁*/
    private final ReentrantLock lock;
    /** 非空等待条件 */
    private final Condition notEmpty;
    /** 非满等待条件 */
    private final Condition notFull;

    // Internal helper methods

    /**
     *数组是循环,如果i == items.length-1 ,那么下一个index =0 从头开始
     */
    final int inc(int i) {
        return (++i == items.length)? 0 : i;
    }

    /**
     * Inserts element at current put position, advances, and signals.
     * Call only when holding lock.
      当前的putIndex位置上放置x元素(只能在获取锁的情况下调用)
     */
    private void insert(E x) {
        items[putIndex] = x;
        putIndex = inc(putIndex); //插入数据后,下一个要插入的位置需要 +1
        ++count; //数量+1
        notEmpty.signal(); //含义:一旦插入新数据,那么需要通知notEmpty.wait 条件下等待的线程(take数据的线程)
    }

    /**
     * Extracts element at current take position, advances, and signals.
     * 取一个元素 (只能在获取锁的情况下调用)
     */
    private E extract() {
        final E[] items = this.items;
        E x = items[takeIndex];// 获取当前takeIndex的元素
        items[takeIndex] = null;
        takeIndex = inc(takeIndex); // 调整下一个获取元素的的位置
        --count; //数量要减一个
        notFull.signal(); //取出后,通知添加数据的线程,items已经不满了。
        return x;
    }

    /**
     * 擅长在i位置的item元素(Call only when holding lock.)
     * 
     */
    void removeAt(int i) {
        final E[] items = this.items;
        // if removing front item, just advance
        if (i == takeIndex) {  //如果i是下一个将要获取的值,那么这个值直接置空即可!!!
            items[takeIndex] = null;
            takeIndex = inc(takeIndex); //增加下一个获取值的位置
        } else {
            // slide over all others up through putIndex.
            for (;;) { //否则
                int nexti = inc(i);
                if (nexti != putIndex) { //next 不等于  下一个要放数据的位置
                    items[i] = items[nexti]; //此处的逻辑是移动i+1 之后的元素到i位置上
                    i = nexti;
                } else { 如果nexti 为 putIndex
                    items[i] = null; //i = nexti 那么i位置置空,putIndex = i
                    putIndex = i;
                    break;
                }
            }
        }
        --count; //数量--
        notFull.signal(); //含义:删除数据后,要通知notFull条件 已经非空,释放一个notFull.wait 的线程
    }

    /**
     * 
     */
    public ArrayBlockingQueue(int capacity) {
        this(capacity, false);
    }


    public ArrayBlockingQueue(int capacity, boolean fair) {
        if (capacity <= 0)
            throw new IllegalArgumentException();
        this.items = (E[]) new Object[capacity];
        lock = new ReentrantLock(fair); //是否是公平锁
        notEmpty = lock.newCondition();//非空的严谨条件
        notFull =  lock.newCondition(); //非满的条件,
        //比如:如果put操作因为空间不足,那么将会一直阻塞(notFull wait),如果这期间没有新数据读取了,那么需要调用:notFull wait方法。
    }

   
    public ArrayBlockingQueue(int capacity, boolean fair,
                              Collection<? extends E> c) {
        this(capacity, fair);
        if (capacity < c.size())
            throw new IllegalArgumentException();

        for (Iterator<? extends E> it = c.iterator(); it.hasNext();)
            add(it.next());
    }

    /**
     * 添加元素
     */
    public boolean add(E e) {
	   return super.add(e);
    }

    /**
     * 添加元素如已满,那么返回false ,否则返回true
     *
     * @throws NullPointerException if the specified element is null
     */
    public boolean offer(E e) {
        if (e == null) throw new NullPointerException();
        final ReentrantLock lock = this.lock;
        lock.lock(); //变更数据结构的操作,必须枷锁,另外count变量也必须在lock加锁的情况下使用
        try {
            if (count == items.length) //如没有空间了,那么返回flase
                return false;
            else {
                insert(e);
                return true;
            }
        } finally {
            lock.unlock();
        }
    }

    /**
     * 在数组的尾部放数据
     */
    public void put(E e) throws InterruptedException {
        if (e == null) throw new NullPointerException();
        final E[] items = this.items;
        final ReentrantLock lock = this.lock;//先加锁
        lock.lockInterruptibly();
        try {
            try {
                while (count == items.length) //如果已经满,那么等待
                    notFull.await();
            } catch (InterruptedException ie) {
                notFull.signal(); // 如果notFull.await 被打断,其实我认为:此处做这个signal 没有太多意义,做了也是白做,因为items.length 、count没有变啊!!!
                throw ie;
            }
            insert(e);
        } finally {
            lock.unlock();
        }
    }

    /**
    插入数据
     */
    public boolean offer(E e, long timeout, TimeUnit unit)
        throws InterruptedException {

        if (e == null) throw new NullPointerException();
	      long nanos = unit.toNanos(timeout);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            for (;;) {
                if (count != items.length) { //如果count != length 就是数组不满呗!那么直接插入即可
                    insert(e);
                    return true;
                }
                if (nanos <= 0)
                    return false;
                try {
                    nanos = notFull.awaitNanos(nanos); //等待nanos纳秒  注意必须这样用!
                } catch (InterruptedException ie) {
                    notFull.signal(); // propagate to non-interrupted thread
                    throw ie;
                }
            }
        } finally {
            lock.unlock();
        }
    }

    public E poll() {
        final ReentrantLock lock = this.lock;
        lock.lock(); //必须枷锁
        try {
            if (count == 0) //取数据,如果没有那么返回null
                return null;
            E x = extract();
            return x;
        } finally {
            lock.unlock();
        }
    }

    public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            try {
                while (count == 0) //如果为空,那么notEmpty  await 进入等待状态
                    notEmpty.await();
            } catch (InterruptedException ie) {
                notEmpty.signal(); // propagate to non-interrupted thread
                throw ie;
            }
            E x = extract();
            return x;
        } finally {
            lock.unlock();
        }
    }

    public E poll(long timeout, TimeUnit unit) throws InterruptedException {
	      long nanos = unit.toNanos(timeout);
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            for (;;) {
                if (count != 0) { //如果还有元素,我以及获得lock锁了,所以count 是不会有竞争的
                    E x = extract();
                    return x;
                }
                if (nanos <= 0)
                    return null;
                try {
                    nanos = notEmpty.awaitNanos(nanos); //如果没有数据那么 wait nanos的时间
                } catch (InterruptedException ie) {
                    notEmpty.signal(); // 传播异常事必要的。但是notEmpty.signal 是完全没必要的
                    throw ie;
                }

            }
        } finally {
            lock.unlock();
        }
    }

    public E peek() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return (count == 0) ? null : items[takeIndex];
        } finally {
            lock.unlock();
        }
    }

    // this doc comment is overridden to remove the reference to collections
    // greater in size than Integer.MAX_VALUE
    /*
     */
    public int size() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try { //返回的count 必须有lock锁 保卫,否则数据事安全的。
            return count;
        } finally {
            lock.unlock();
        }
    }

    // 返回还剩下的元素
    
    public int remainingCapacity() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return items.length - count;
        } finally {
            lock.unlock();
        }
    }

    /**
     * Removes a single instance of the specified element from this queue,
      删除一个在queue中的元素
     * 
     */
    public boolean remove(Object o) {
        if (o == null) return false;
        final E[] items = this.items;
        final ReentrantLock lock = this.lock;
        lock.lock(); //删除元素需要加锁
        try {
            int i = takeIndex; //记录的要取数据的位置
            int k = 0; //记录已经取得的元素数量
            for (;;) {
                if (k++ >= count)//如果已经遍历的元素数量k 超过count ,那么可以认定已经找完,没有找到 o 数据。
                    return false;
                if (o.equals(items[i])) { //如果o == items[i] ,那么删除i位置的元素
                    removeAt(i);
                    return true;
                }
                i = inc(i); //如果没有找到,那么找下一个!!
            }

        } finally {
            lock.unlock();
        }
    }

    public boolean contains(Object o) {
        if (o == null) return false;
        final E[] items = this.items;
        final ReentrantLock lock = this.lock; //为了防止遍历中,有数据结构的变动
        lock.lock();
        try {
            int i = takeIndex;
            int k = 0;
            while (k++ < count) { //如果还有元素
                if (o.equals(items[i])) //找到一个可以和在以前的人,那么可以直接返回
                    return true;
                i = inc(i);
            }
            return false;
        } finally {
            lock.unlock();
        }
    }

   
    public Object[] toArray() {
        final E[] items = this.items;
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            Object[] a = new Object[count];
            int k = 0;
            int i = takeIndex;
            while (k < count) {
                a[k++] = items[i];
                i = inc(i);
            }
            return a;
        } finally {
            lock.unlock();
        }
    }

    public <T> T[] toArray(T[] a) {
        final E[] items = this.items;
        final ReentrantLock lock = this.lock; //复制需要加锁!
        lock.lock();
        try {
            if (a.length < count)
                a = (T[])java.lang.reflect.Array.newInstance( //生成数组实例的方法!!
                    a.getClass().getComponentType(), //数组的
                    count //数量
                    );

            int k = 0;
            int i = takeIndex; //取元素的职位,tckeIndex不变的,不能变的!!
            while (k < count) { //如果k<count 那么说明还有数据!!
                a[k++] = (T)items[i];
                i = inc(i);
            }
            if (a.length > count)
                a[count] = null; //做一个特殊处理,不处理也可以的!
            return a;
        } finally {
            lock.unlock();
        }
    }

    public String toString() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return super.toString();
        } finally {
            lock.unlock();
        }
    }

    /**
     * 情况缓冲
     */
    public void clear() {
        final E[] items = this.items;
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            int i = takeIndex; //取元素的位置
            int k = count; //总元素的数量
            while (k-- > 0) {
                items[i] = null;
                i = inc(i); //让下一个元素的位置+1
            }
            count = 0;
            putIndex = 0;
            takeIndex = 0;
            notFull.signalAll(); //非满的条件通知
        } finally {
            lock.unlock();
        }
    }

    /**
     *  把队列中的item放到c 容器中
   */
    public int drainTo(Collection<? super E> c) {
        if (c == null)
            throw new NullPointerException();
        if (c == this)
            throw new IllegalArgumentException();
        final E[] items = this.items;
        final ReentrantLock lock = this.lock;
        lock.lock(); //先获得锁
        try {
            int i = takeIndex;
            int n = 0;
            int max = count; //指定最大count
            while (n < max) {
                c.add(items[i]);
                items[i] = null;
                i = inc(i);
                ++n;
            }
            if (n > 0) { //如果n >0 ,其实就是说count >0,那么把相关值重置
                count = 0;
                putIndex = 0;
                takeIndex = 0;
                notFull.signalAll(); //此处的signalAll 是必须的,而不能是signal !,他要通知到所有的处于put等待状态的线程!!!
            }
            return n;
        } finally {
            lock.unlock();
        }
    }

    /**
     * 把队列中的item放到c 容器中
     */
    public int drainTo(Collection<? super E> c, int maxElements) {
        if (c == null)
            throw new NullPointerException();
        if (c == this)
            throw new IllegalArgumentException();
        if (maxElements <= 0)
            return 0;
        final E[] items = this.items;
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            int i = takeIndex;
            int n = 0;
            int sz = count;
            int max = (maxElements < count)? maxElements : count;
            while (n < max) {  /
                c.add(items[i]);
                items[i] = null;
                i = inc(i);
                ++n;
            }
            if (n > 0) {
                count -= n;
                takeIndex = i;
                notFull.signalAll();
            }
            return n;
        } finally {
            lock.unlock();
        }
    }


    /**
     * 生成一个迭代器
     *
     * @return an iterator over the elements in this queue in  proper sequence
     */
    public Iterator<E> iterator() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return new Itr(); //   必须枷锁的情况下完成
        } finally {
            lock.unlock();
        }
    }

    /**
     * Iterator for ArrayBlockingQueue
     */
    private class Itr implements Iterator<E> {
        /**
         * 下一个返回的元素的index值
         */
        private int nextIndex;

        /**
         * 下一个元素
         */
        private E nextItem;

        /**
         * 返回上一次返回的记录的位置,如果上一个操作是删除操作,那么lastRet值为-1
         */
        private int lastRet;

        Itr() {
            lastRet = -1; //初始化为-1
            if (count == 0)
                nextIndex = -1; //如果没有元素,那么nextIndex也置为-1
            else { //否则赋相应的值
                nextIndex = takeIndex; 
                nextItem = items[takeIndex];
            }
        }

        public boolean hasNext() {
            /*
             * No sync. We can return true by mistake here
             * only if this iterator passed across threads,
             * which we don't support anyway.

             注意:此方法事不同步的!! 在多线程环境下 可能错误的返回true,nextIndex  只能标志:读取下一个元素从哪儿开始。  jdk不对同步做支持,其事实上即便支持也没什么实际意义!!!           */
            return nextIndex >= 0;
        }

        /**
         * 如果 nextIndex 是有效的,那么设置nextItem值
         */
        private void checkNext() {
            if (nextIndex == putIndex) { //已经put的位置了,已经没有数据可取
                nextIndex = -1;
                nextItem = null;
            } else {
                nextItem = items[nextIndex];
                if (nextItem == null) 
                    nextIndex = -1;
            }
        }

/* [b]获取下一个元素: 其实此方法我感觉几乎没什么意义! 不准确,还可能抛异常,还不如toArray 得了[/b]*/
        public E next() {
            final ReentrantLock lock = ArrayBlockingQueue.this.lock;
            lock.lock();
            try {
                if (nextIndex < 0)
                    throw new NoSuchElementException();
                lastRet = nextIndex;
                E x = nextItem;
                nextIndex = inc(nextIndex);
                checkNext();
                return x;
            } finally {
                lock.unlock();
            }
        }

/** 同上*/
        public void remove() {
            final ReentrantLock lock = ArrayBlockingQueue.this.lock;
            lock.lock();
            try {
                int i = lastRet;
                if (i == -1)
                    throw new IllegalStateException();
                lastRet = -1;

                int ti = takeIndex;
                removeAt(i);
                // back up cursor (reset to front if was first element)
                nextIndex = (i == ti) ? takeIndex : i;
                checkNext();
            } finally {
                lock.unlock();
            }
        }
    }
}