J.U.C之BlockingQueue

BlockingQueue

在分析BlockingQueue之前,先看一下JDK1.8中对于BlockingQueue的注释说明:

A {@link java.util.Queue} that additionally supports operations that wait for the queue to become non-empty when retrieving an element, and wait for space to become available in the queue when storing an element.

即获取元素的时候等待队列变为非空,以及存储元素的时候等待队列变为可用。

BlockingQueue有这几个特点:不接受null元素、可能是容量有限的、主要用作生产者-消费者队列、线程安全等。

BlockingQueue同时提供阻塞和非阻塞的方法,例如:

boolean offer(E e);
boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException;

前者立即返回插入结果,后者可以在超时时间之内等待队列可用的时候再执行插入操作。

在JDK1.8中提供了BlockingQueue的七种实现以及一种ScheduledThreadPoolExecutor的内部实现:

ArrayBlockingQueue :一个由数组结构组成的有界阻塞队列。

LinkedBlockingQueue :一个由链表结构组成的无界阻塞队列。

PriorityBlockingQueue :一个支持优先级排序的无界阻塞队列。

DelayQueue:一个使用优先级队列实现的无界阻塞队列。

SynchronousQueue:一个不存储元素的阻塞队列。

LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。

LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。

接下来将一个个分析每一种队列。

ArrayBlockingQueue

ArrayBlockingQueue是基于数组的队列,构造函数初始化的时候生成定长数组,基于FIFO的原则对数组元素进行操作。

ArrayBlockingQueue内部的阻塞队列是通过重入锁ReenterLock和Condition条件队列实现的,所以ArrayBlockingQueue中的元素存在公平访问与非公平访问的区别,对于公平访问队列,被阻塞的线程可以按照阻塞的先后顺序访问队列,即先阻塞的线程先访问队列。而非公平队列,当队列可用时,阻塞的线程将进入争夺访问资源的竞争中,也就是说谁先抢到谁就执行,没有固定的先后顺序。 

public class ArrayBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, java.io.Serializable {
    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;

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

ArrayBlockingQueue的入列操作:

add(E e) :将指定的元素插入到此队列的尾部(如果立即可行且不会超过该队列的容量),在成功时返回 true,如果此队列已满,则抛出 IllegalStateException

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

offer(E e, long timeout, TimeUnit unit) :将指定的元素插入此队列的尾部,如果该队列已满,则在到达指定的等待时间之前等待可用的空间,超时返回 false

put(E e) :将指定的元素插入此队列的尾部,如果该队列已满,则等待可用的空间

四个方法都是通过调用enqueue(E e)方法将元素插入到数组的:

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

enqueue方法就是在putIndex位置添加元素,如果putIndex+1位于队尾则移动到队伍头部,并且通知阻塞在等待出列状态的线程。

 

ArrayBlockingQueue的出列操作:

poll() :获取并移除此队列的头,如果此队列为空,则返回 null

poll(long timeout, TimeUnit unit) :获取并移除此队列的头部,在指定的等待时间前等待可用的元素(如果有必要)

take() :获取并移除此队列的头部,在元素变得可用之前一直等待(如果有必要)

remove(Object o) :从此队列中移除指定元素的单个实例(如果存在)

 

前三个方法使用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;
    }

dequeue方法就是在takeIndex的位置取出元素(元素置空),takeIndex的位置后移一位,同时维护迭代期 itrs 对象的信息,最后通知阻塞在等待入列状态的线程。

remove方法,先遍历找到元素,如果找到则调用私有方法removeAt(final int removeIndex),方法从removeIndex到putIndex,一个个前移元素,最后通知阻塞在等待入列的线程。

LinkedBlockingQueue

LinkedBlockingQueue是一个由链表实现的有界队列阻塞队列,但大小默认值为Integer.MAX_VALUE,所以我们在使用LinkedBlockingQueue时建议手动传值,为其提供我们所需的大小,避免队列过大造成机器负载或者内存爆满等情况。其构造函数如下:

//默认大小为Integer.MAX_VALUE
public LinkedBlockingQueue() {
       this(Integer.MAX_VALUE);
}

//创建指定大小为capacity的阻塞队列
public LinkedBlockingQueue(int capacity) {
     if (capacity <= 0) throw new IllegalArgumentException();
     this.capacity = capacity;
     last = head = new Node<E>(null);
 }

//创建大小默认值为Integer.MAX_VALUE的阻塞队列并添加c中的元素到阻塞队列
public LinkedBlockingQueue(Collection<? extends E> c) {
     this(Integer.MAX_VALUE);
     final ReentrantLock putLock = this.putLock;
     putLock.lock(); // Never contended, but necessary for visibility
     try {
         int n = 0;
         for (E e : c) {
             if (e == null)
                 throw new NullPointerException();
             if (n == capacity)
                 throw new IllegalStateException("Queue full");
             enqueue(new Node<E>(e));
             ++n;
         }
         count.set(n);
     } finally {
         putLock.unlock();
     }
 }

 

猜你喜欢

转载自www.cnblogs.com/CHMaple/p/9284583.html