`

源码剖析之ArrayBlockingQueue

阅读更多
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();
            }
        }
    }
}


2
0
分享到:
评论
1 楼 nizen 2013-11-07  
                notFull.signal(); // 如果notFull.await 被打断,其实我认为:此处做这个signal 没有太多意义,做了也是白做,因为items.length 、count没有变啊!!! 

这里设A1,A2线程在put时因full而await(),如B线程take后对A1线程进行signal(),C线程对A1进行interrupt(),那么A1线程可能会先消化了signal(),再通过interrupt()返回,此时队列非满,而A2线程却一直在await()!

相关推荐

    ArrayBlockingQueue源码分析.docx

    下面我们将深入分析其主要的实现机制、方法以及源码。 1. **数据结构与容量** `ArrayBlockingQueue` 内部使用一个数组 `items` 来存储元素,因此它的容量在创建时就需要指定,且不可改变。这个容量限制确保了队列...

    并发容器之ArrayBlockingQueue和LinkedBlockingQueue实现原理详解

    "并发容器之ArrayBlockingQueue和LinkedBlockingQueue实现原理详解" ArrayBlockingQueue和LinkedBlockingQueue是Java并发容器中两个常用的阻塞队列实现,分别基于数组和链表存储元素。它们都继承自AbstractQueue类...

    ArrayBlockingQueue源码解析-动力节点共

    在本文中,我们将深入分析ArrayBlockingQueue的源码,探讨其内部实现机制、特性以及如何在实际应用中高效地使用它。 首先,ArrayBlockingQueue是一个有界的阻塞队列,这意味着它有一个固定的容量限制。在创建时必须...

    Java concurrency集合之ArrayBlockingQueue_动力节点Java学院整理

    Java concurrency集合之ArrayBlockingQueue_动力节点Java学院整理,动力节点口口相传的Java黄埔军校

    java并发之ArrayBlockingQueue详细介绍

    Java并发之ArrayBlockingQueue详细介绍 ArrayBlockingQueue是Java并发编程中常用的线程安全队列,经常被用作任务队列在线程池中。它是基于数组实现的循环队列,具有线程安全的实现。 ArrayBlockingQueue的实现 ...

    Java源码解析阻塞队列ArrayBlockingQueue常用方法

    为了实现线程安全,ArrayBlockingQueue使用了一个可重入锁`ReentrantLock`以及两个与之关联的条件变量`notEmpty`和`notFull`。`notEmpty`用于等待队列非空时的线程,而`notFull`则是等待队列未满时的线程。 在了解...

    Java源码解析阻塞队列ArrayBlockingQueue介绍

    Java源码解析阻塞队列ArrayBlockingQueue介绍 Java源码解析阻塞队列ArrayBlockingQueue介绍是Java中的一种阻塞队列实现,使用ReentrantLock和Condition来实现同步和阻塞机制。本文将对ArrayBlockingQueue的源码进行...

    Java源码解析阻塞队列ArrayBlockingQueue功能简介

    Java源码解析阻塞队列ArrayBlockingQueue功能简介 ArrayBlockingQueue是Java中一个重要的阻塞队列实现,它基于数组实现了有界阻塞队列,提供FIFO(First-In-First-Out)功能。该队列的头元素是最长时间呆在队列中的...

    Java并发包源码分析(JDK1.8)

    囊括了java.util.concurrent包中大部分类的源码分析,其中涉及automic包,locks包(AbstractQueuedSynchronizer、ReentrantLock、ReentrantReadWriteLock、LockSupport等),queue(ArrayBlockingQueue、...

    详细分析Java并发集合ArrayBlockingQueue的用法

    Java并发集合ArrayBlockingQueue的用法详解 Java并发集合ArrayBlockingQueue是Java并发集合框架下的一个重要组件,它提供了阻塞队列的实现,用于多线程环境下的并发操作。下面是对ArrayBlockingQueue的用法详解: ...

    26不让我进门,我就在门口一直等!—BlockingQueue和ArrayBlockingQueue.pdf

    —BlockingQueue和ArrayBlockingQueue.pdf” 【描述】:此文档是关于Java并发编程的学习资料,以漫画形式讲解,聚焦于Java并发编程中的核心概念——BlockingQueue接口及其具体实现ArrayBlockingQueue。 【标签】:...

    java中LinkedBlockingQueue与ArrayBlockingQueue的异同

    这两种队列在很多方面都有相似之处,但在实现机制和性能特性上存在一些差异。 **相同点** 1. **接口实现**:两者都是`BlockingQueue`接口的实现,提供了`put`、`take`等阻塞操作,使得生产者线程在队列满时会被...

    Java可阻塞队列-ArrayBlockingQueue

    在前面的的文章,写了一个带有缓冲区的队列,是用JAVA的Lock下的Condition实现的,但是JAVA类中提供了这项功能,是ArrayBlockingQueue,  ArrayBlockingQueue是由数组支持的有界阻塞队列,次队列按照FIFO(先进先...

    java并发源码分析之实战编程

    "java并发源码分析之实战编程"这个主题深入探讨了Java平台上的并发处理机制,旨在帮助开发者理解并有效地利用这些机制来提高程序性能和可扩展性。在这个专题中,我们将围绕Java并发库、线程管理、锁机制、并发容器...

    【死磕Java集合】-集合源码分析.pdf

    八、ArrayBlockingQueue源码分析 ArrayBlockingQueue是一种基于数组实现的阻塞队列,提供了线程安全的生产者消费者模型。ArrayBlockingQueue的继承体系中,它继承了AbstractQueue,实现了BlockingQueue接口。 ...

    免费开源-【Java学习+面试指南】部分内容大部分是Java程序员所需要掌握的核心知识

    ArrayList核心源码+扩容机制分析LinkedList核心源码分析HashMap核心源码+底层数据结构分析ConcurrentHashMap核心源码+底层数据结构分析LinkedHashMap核心源码分析CopyOnWriteArrayList核心源码分析...

Global site tag (gtag.js) - Google Analytics