[JDK1.6] JAVA集合 ArrayBlockingQueue 源码浅析 (同步/阻塞队列)

文章目录

  • [一] 简介:
  • [二] 关键属性:
  • [三] 初始化
  • [四] ArrayBlockingQueue 核心方法
    • 插入元素
    • 取出元素
    • (阻塞) put
    • (阻塞) take
    • (非阻塞) offer(E)
    • (非阻塞) poll()
  • 线程的等待与唤醒
  • 迭代器 Iterator
    • iterator 返回迭代器
    • Itr 迭代器实现类

注释来自java api
源码来自 JDK1.6

[一] 简介:

一个由数组支持的有界阻塞队列。此队列按 FIFO(先进先出)原则对元素进行排序。队列的头部 是在队列中存在时间最长的元素。队列的尾部 是在队列中存在时间最短的元素。新元素插入到队列的尾部,队列获取操作则是从队列头部开始获得元素。

这是一个典型的“有界缓存区”,固定大小的数组在其中保持生产者插入的元素和使用者提取的元素。一旦创建了这样的缓存区,就不能再增加其容量。试图向已满队列中放入元素会导致操作受阻塞;试图从空队列中提取元素将导致类似阻塞。

此类支持对等待的生产者线程和使用者线程进行排序的可选公平策略。默认情况下,不保证是这种排序。然而,通过将公平性 (fairness) 设置为 true 而构造的队列允许按照 FIFO 顺序访问线程。公平性通常会降低吞吐量,但也减少了可变性和避免了“不平衡性”。

此类及其迭代器实现了 Collection 和 Iterator 接口的所有可选 方法。

此类是 Java Collections Framework 的成员。

注意: ArrayBlockingQueue 的容量一旦初始化大小将不会改变, 即不会有扩容的策略
架构

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

[JDK1.6] JAVA集合 ArrayBlockingQueue 源码浅析 (同步/阻塞队列)_第1张图片

[二] 关键属性:

属性 说明
items 存放元素队列(数组)
takeIndex 下一个取出元素的索引
putIndex 下一个存放元素的索引
count 所有元素的数量
lock 线程锁, 用来控制获取和存放的操作
notEmpty 用于取出元素时,如果队列没有元素,让线程等待释放锁的对象(进入阻塞状态)
notFull 用于存入元素时,如果线程满了, 让线程等待释放锁的对象 (进入阻塞状态)
    /** 存放元素队列 */
    private final E[] items;
    /** 下一个元素取出的索引, 方法为 take, poll or remove */
    private int takeIndex;
    /** 下一个元素存放的索引, 方法为 put, offer, or add. */
    private int putIndex;
    /** 队列元素数量 */
    private int count;

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

    /** lock, 控制所有的访问*/
    private final ReentrantLock lock;
    /** Condition 对应等待获取*/
    private final Condition notEmpty;
    /** Condition 对应等待放入*/
    private final Condition notFull;

[三] 初始化

源码注释: 创建一个带有给定的(固定)容量和默认访问策略的 ArrayBlockingQueue。

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

源码注释: 创建一个具有给定的(固定)容量和指定访问策略的 ArrayBlockingQueue。
fair - 如果为 true,则按照 FIFO 顺序访问插入或移除时受阻塞线程的队列;如果为 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();
    }

源码注释: 创建一个具有给定的(固定)容量和指定访问策略的 ArrayBlockingQueue,它最初包含给定 collection 的元素,并以 collection 迭代器的遍历顺序添加元素。

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

[四] ArrayBlockingQueue 核心方法

插入元素

源码注释: 在当前放置位置,前进和信号处插入元素。
只有获取了锁,才能调用。

    private void insert(E x) {
        items[putIndex] = x;
        putIndex = inc(putIndex);
        ++count;
        notEmpty.signal();
    }

循环增量

    final int inc(int i) {
        return (++i == items.length)? 0 : i;
    }

取出元素

源码注释: 在当前获取位置,前进和信号处提取元素。
只有获取了锁,才能调用。

    private E extract() {
        final E[] items = this.items;
        E x = items[takeIndex];
        items[takeIndex] = null;
        takeIndex = inc(takeIndex);
        --count;
        notFull.signal();
        return x;
    }

(阻塞) put

当队列满 full 时, 进入阻塞状态

    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(); // propagate to non-interrupted thread
                throw ie;
            }
            insert(e);
        } finally {
            lock.unlock();
        }
    }

(阻塞) take

当队列为空isEmpty 时, 进入阻塞状态

    public E take() throws InterruptedException {
        final ReentrantLock lock = this.lock;
        lock.lockInterruptibly();
        try {
            try {
                while (count == 0)
                    notEmpty.await();
            } catch (InterruptedException ie) {
                notEmpty.signal(); // propagate to non-interrupted thread
                throw ie;
            }
            E x = extract();
            return x;
        } finally {
            lock.unlock();
        }
    }

(非阻塞) offer(E)

源码注释: 将指定的元素插入到此队列的尾部(如果立即可行且不会超过该队列的容量),在成功时返回 true,如果此队列已满,则返回 false。

    public boolean offer(E e) {
        if (e == null) throw new NullPointerException();
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            if (count == items.length)
                return false;
            else {
                insert(e);
                return true;
            }
        } finally {
            lock.unlock();
        }
    }

(非阻塞) poll()

源码注释: 获取并移除此队列的头,如果此队列为空,则返回 null。

    public E poll() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            if (count == 0)
                return null;
            E x = extract();
            return x;
        } finally {
            lock.unlock();
        }
    }

线程的等待与唤醒

调用图
[JDK1.6] JAVA集合 ArrayBlockingQueue 源码浅析 (同步/阻塞队列)_第2张图片

  1. 在调用 put方法时, 如果队列满了, 会调用 notFull.await() 方法让线程进入等待状态;
  2. 往队列存放一个元素时,最终调用 insert() ,完成后会唤醒一个线程, notEmpty.signal();
  3. 在调用 take方法时, 如果队列为空, 会调用 notEmpty.await() 方法让线程进入等待状态;
  4. 从队列取出一个元素是, 最终调用 extract()方法, 完成后会唤醒一个线程, notFull.signal();
  5. put 等待的线程 notFull.await(),由 extract() 方法唤醒 notFull.signal();
  6. take 等待的线程 notEmpty.await(), 由 insert() 方法唤醒 notEmpty.signal();

迭代器 Iterator

iterator 返回迭代器

    public Iterator<E> iterator() {
        final ReentrantLock lock = this.lock;
        lock.lock();
        try {
            return new Itr();
        } finally {
            lock.unlock();
        }
    }

Itr 迭代器实现类

private class Itr implements Iterator<E> {

        private int remaining; // 尚未返回的元素数量
        private int nextIndex; // 下一个要返回的元素的索引
        private E nextItem;    // 下一次调用将返回的元素
        private E lastItem;    // 最后一次调用返回的元素
        private int lastRet;   // 返回的最后一个元素的索引,如果没有则返回-1

        Itr() {
            lastRet = -1;
            if ((remaining = count) > 0)
                nextItem = items[nextIndex = takeIndex];
        }
        
        public boolean hasNext() {
            return remaining > 0;
        }
        
        public E next() {
            if (remaining <= 0)
                throw new NoSuchElementException();
            final ReentrantLock lock = ArrayBlockingQueue.this.lock;
            lock.lock();
            try {
                lastRet = nextIndex;
                E x = lastItem = nextItem;
                while (--remaining > 0) {
                    if ((nextItem = items[nextIndex = inc(nextIndex)]) != null)
                        break;
                }
                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;
                E x = lastItem;
                lastItem = null;
                // only remove if item still at index
                if (x == items[i]) {
                    boolean removingHead = (i == takeIndex);
                    removeAt(i);
                    if (!removingHead)
                        nextIndex = dec(nextIndex);
                }
            } finally {
                lock.unlock();
            }
        }
}

你可能感兴趣的:(JAVA集合,java集合)