ArrayBlockingQueue解析

来源:互联网 发布:长沙理工大学网络认证 编辑:程序博客网 时间:2024/06/04 23:36

ArrayBlockingQueue解析

继承关系

java.util.concurrent
类 ArrayBlockingQueue<E>
java.lang.Object
     继承者 java.util.AbstractCollection<E>
          继承者 java.util.AbstractQueue<E>
               继承者 java.util.concurrent.ArrayBlockingQueue<E>
类型参数:
     E - 在此 collection 中保持的元素类型
所有已实现的接口:
     Serializable, Iterable<E>, Collection<E>, BlockingQueue<E>, Queue<E>

源码解析

类声明
public class ArrayBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, java.io.Serializable{}
由代码以及上面继承关系可以看出,ArrayBlockingQueue是一个阻塞队列,其根本就是一个队列,队列就要具备队列的特性。
* 队列是一个钟特殊的线性表,只允许在队头进行删除操作,在队尾进行插入操作
* 队列中没有元素时,称为空队列。
* 在队列中插入一个队列元素称为入队,从队列中删除一个队列元素成为出队。队列是一种先进先出(FIFO)线性表。

先看一下源码注释,翻译过来之后大概是这样的:
ArrayBlockingQueue是一个有数组支持的有界阻塞队列。该队列按照FIFO(先进先出)规则对元素进行排序,队列的头部元素是队列中存在时间最长的元素。队列的尾部元素是队列中存在时间最短的元素。新元素插入到队列的尾部,队列检索操作则是从队列头部开始获得元素。
这是一个典型的“有界缓存区”,固定大小的数组在其中保存着生产者插入的元素和消费者提取的元素。一旦创建,缓存区容量就不可再改变。试图向一个满队列中放入元素会导致该操作阻塞;试图从空队列中获得元素将一样导致阻塞。
此类支持对等待生产者现成和消费者现成进行排序的可选公平策略。默认情况下,不保证是这种排序。然而,通过设置公平性(faireness)为true而构造的队列允许线程按照FIFO顺序访问。公平性通常会降低吞吐量,但也减少了可变性,同时避免了“不平衡性”。
此类和它的迭代器实现了Collection和Iterator接口的所有可选方法。
此类是Java Collection Framework的成员。

源码分析

先看一下构造器:
ArrayBlockingQueue提供了3个构造方法,第一个构造方法有一个参数,即队列大小。

    /**     * 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}     */    public ArrayBlockingQueue(int capacity) {        this(capacity, false);    }

第二个构造方法有两个参数,capacity和fair,capacity同第一个构造方法,代表队列大小。fair代表该队列的访问策略是否公平。如果为 true,则按照 FIFO 顺序访问插入或移除时受阻塞线程的队列;如果为 false,则访问顺序是不确定的。这里fair参数被设置为ReentrantLock的入参,就可以通过ReentrantLock来保证线程访问是否公平。而此构造方法创建了两个Condition,也就是条件,分别是notEmpty和notFull,Condition可以调用wait()和signal()来控制当前现成等待或者唤醒。

    /**     * 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.     * @throws IllegalArgumentException if {@code capacity < 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();    }

第三个构造方法如下,提供了三个参数,前两个和上面的参数相同,第三个是一个集合,在构造阻塞队列时将集合中的元素初始化放入阻塞队列。此时队列中有了初始化数据。

    /**     * 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.     * @param c the collection of elements to initially contain     * @throws IllegalArgumentException if {@code capacity} is less than     *         {@code c.size()}, or less than 1.     * @throws NullPointerException if the specified collection or any     *         of its elements are 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) {                throw new IllegalArgumentException();            }            count = i;            putIndex = (i == capacity) ? 0 : i;        } finally {            lock.unlock();        }    }

再来看几个常用方法。看一下put方法,该方法向对位插入元素,如果队列已满则线程阻塞。而其中使用了insert方法,在insert方法中,向队列插入元素,插入之后,则唤醒获取元素时阻塞的线程。

    /**     * 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}     */    public void put(E e) throws InterruptedException {        checkNotNull(e);        final ReentrantLock lock = this.lock;        lock.lockInterruptibly();        try {            while (count == items.length)                notFull.await();            insert(e);        } finally {            lock.unlock();        }    }     /**     * Inserts element at current put position, advances, and signals.     * Call only when holding lock.     */    private void insert(E x) {        items[putIndex] = x;        putIndex = inc(putIndex);        ++count;        notEmpty.signal();    }

ArrayBlockingQueue提供了两个offer方法,第一个offer方法有一个参数,就是插入到队尾的一个元素,如果队列已满,则不插入,返回false,如果队列有空位,则插入,返回true。第二个offer方法有3个参数,第一个参数和第一个offer方法参数相同,第二个参数timeout,即超时时间,也就是在该时间范围内,如果有空位则插入,返回true,如果没有,则返回false。

 /**     * 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.     *     * @throws NullPointerException if the specified element is null     */    public boolean offer(E e) {        checkNotNull(e);        final ReentrantLock lock = this.lock;        lock.lock();        try {            if (count == items.length)                return false;            else {                insert(e);                return true;            }        } finally {            lock.unlock();        }    }    /**     * 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}     */    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 {            while (count == items.length) {                if (nanos <= 0)                    return false;                nanos = notFull.awaitNanos(nanos);            }            insert(e);            return true;        } finally {            lock.unlock();        }    }

take方法是在对头元素移除并返回,如果一直都不可用,则一直等待。

    public E take() throws InterruptedException {        final ReentrantLock lock = this.lock;        lock.lockInterruptibly();        try {            while (count == 0)                notEmpty.await();            return extract();        } finally {            lock.unlock();        }    }

来看一下获取元素的另外两个方法,poll方法,第一个poll方法,获取并移除此队列的头,如果此队列为空,则返回 null。第二个poll方法,获取并移除此队列的头部,在指定的等待时间前等待可用的元素(如果有必要)。这两个方法和offer方法类似效果。peek方法同样可以获取元素,获取但不移除此队列的头;如果此队列为空,则返回 null。

    public E poll() {        final ReentrantLock lock = this.lock;        lock.lock();        try {            return (count == 0) ? null : dequeue();        } 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 {            while (count == 0) {                if (nanos <= 0)                    return null;                nanos = notEmpty.awaitNanos(nanos);            }            return dequeue();        } finally {            lock.unlock();        }    }    public E peek() {        final ReentrantLock lock = this.lock;        lock.lock();        try {            return itemAt(takeIndex); // null when queue is empty        } finally {            lock.unlock();        }    }
0 0