简介
ArrayBlockingQueue是一个用数组实现的有界阻塞队列,此队列按照先进先出(FIFO)的原则对元素进行排序。
ArrayBlockingQueue默认情况下不保证线程公平地访问队列,即阻塞的线程,不一定按阻塞的先后顺序访问队列,非公平性也是为了提高吞吐率。ArrayBlockingQueue有三个构造方法:
// capacity是该阻塞队列的容量大小,该构造方法会调用this(capacity, false),即默认为非公平访问策略 public ArrayBlockingQueue(int capacity) // 该方法可以指定访问策略的公平性 public ArrayBlockingQueue(int capacity, boolean fair) // 该方法除了上述功能,还可以将集合中的元素加入到阻塞队列中 public ArrayBlockingQueue(int capacity, boolean fair, Collection<? extends E> c)
ArrayBlockingQueue源码详解
ArrayBlockingQueue类定义为:
public class ArrayBlockingQueue<E> extends AbstractQueue<E> implements BlockingQueue<E>, java.io.Serializable
该类继承自AbstractQueue抽象类,又实现了BlockingQueue接口,AbstractQueue类提供了对队列操作的基础实现:
public abstract class AbstractQueue<E> extends AbstractCollection<E> implements Queue<E> { protected AbstractQueue() { } public boolean add(E e) { if (offer(e)) return true; else throw new IllegalStateException("Queue full"); } public E remove() { E x = poll(); if (x != null) return x; else throw new NoSuchElementException(); } public E element() { E x = peek(); if (x != null) return x; else throw new NoSuchElementException(); } ... }
BlockingQueue接口是阻塞队列的核心接口,该接口定义了阻塞队列中出队、入队的基本方法:
public interface BlockingQueue<E> extends Queue<E> { boolean add(E e); boolean offer(E e); void put(E e) throws InterruptedException; boolean offer(E e, long timeout, TimeUnit unit) throws InterruptedException; E take() throws InterruptedException; E poll(long timeout, TimeUnit unit) throws InterruptedException; int remainingCapacity(); boolean remove(Object o); public boolean contains(Object o); int drainTo(Collection<? super E> c); int drainTo(Collection<? super E> c, int maxElements); }
ArrayBlockingQueue类内部通过一个Object数组来保存数据元素,同时通过ReentrantLock和Condition来确保多线程环境下的同步问题。
/** 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;
- items:一个定长数组,保存队列中的元素
- takeIndex:ArrayBlockingQueue队首位置
- putIndex:ArrayBlockingQueue队尾位置
- count:元素个数
- lock:ArrayBlockingQueue出队入队都必须获取该锁
- notEmpty:出队条件
- notFull:入队条件
一般来说,阻塞队列会提供4种方法来进行入队、出队操作:
方法/处理方式 |
抛出异常 |
返回特殊值 |
一直阻塞 |
超时退出 |
入队方法 |
add(e) |
offer(e) |
put(e) |
offer(e,time,unit) |
出队方法 |
remove() |
poll() |
take() |
poll(time,unit) |
检查方法 |
element() |
peek() |
不可用 |
不可用 |
抛出异常:当队列满时,如果再往队列插入元素,会抛出IllegalStateException("Queue full")异常。当队列空时,从队列获取元素会抛出NoSuchElementException异常。
返回特殊值:当往队列插入元素时,会返回元素插入是否成功,成功返回true。从队列获取元素时,如果没有则返回null。
一直阻塞:当队列满时,如果再往队列插入元素,队列会一直阻塞线程,直到队列可用或响应中断退出。当队列空时,从队列获取元素会阻塞线程,直到队列不为空。
超时退出:在阻塞的基础上,加入了超时等待时间。
下面,我们简单看一看ArrayBlockingQueue类的入队出队操作。
入队
我们这边只来分析一下add(E e)方法:
public boolean add(E e) { return super.add(e); } // AbstractQueue方法 public boolean add(E e) { if (offer(e)) return true; else throw new IllegalStateException("Queue full"); }
该方法最终会调用offer(E e)方法,如果方法返回false,则直接抛出IllegalStateException异常。ArrayBlockingQueue类对offer(E e)方法的实现为:
public boolean offer(E e) { checkNotNull(e); final ReentrantLock lock = this.lock; // 获取独占锁 lock.lock(); try { // 队列已满 if (count == items.length) return false; else { // 入队 enqueue(e); return true; } } finally { // 释放独占锁 lock.unlock(); } }
首先检查插入的元素是否为null,若是则抛出NullPointerException异常。否则获取独占锁,获取锁成功后,如果队列已满则直接返回false,否则调用enqueue(E e)方法:
private void enqueue(E x) { // assert lock.getHoldCount() == 1; // assert items[putIndex] == null; // 入队并更新putIndex的值 final Object[] items = this.items; items[putIndex] = x; if (++putIndex == items.length) putIndex = 0; count++; // 唤醒阻塞在非空条件上的线程 notEmpty.signal(); }
出队
我们这边只来分析一下poll()方法:
public E poll() { final ReentrantLock lock = this.lock; // 获取独占锁 lock.lock(); try { return (count == 0) ? null : dequeue(); } finally { // 释放独占锁 lock.unlock(); } }
如果队列为空返回null,否则调用dequeue()获取队列首元素:
private E dequeue() { // assert lock.getHoldCount() == 1; // assert items[takeIndex] != null; final Object[] items = this.items; @SuppressWarnings("unchecked") // 获取队列首元素并更新takeIndex E x = (E) items[takeIndex]; items[takeIndex] = null; if (++takeIndex == items.length) takeIndex = 0; count--; // 更新itrs迭代器 if (itrs != null) itrs.elementDequeued(); // 唤醒阻塞在非满条件上的线程 notFull.signal(); return x; }
相关博客
参考资料
方腾飞:《Java并发编程的艺术》