ArrayBlockingQueue是一个基于用数组实现的有界阻塞队列。此队列按照先进先出的原则对元素进行排序
一、成员变量
//存放元素的数组 final Object[] items; //队头 int takeIndex; //队尾 int putIndex; //队列中的元素个数 int count; // final ReentrantLock lock; //监视队列是否为空的监视器 private final Condition notEmpty; //监视队列是否到达容量的监视器 private final Condition notFull;
二、构造方法
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(); }
根据传入的fair的值来构造公平还是非公平的容量为capacity的阻塞队列
public ArrayBlockingQueue(int capacity) { this(capacity, false); }
只传入容量的构造方法构造为非公平的阻塞队列
三、操作方法
private void enqueue(E x) { final Object[] items = this.items; items[putIndex] = x; if (++putIndex == items.length) putIndex = 0; count++; notEmpty.signal(); }
入队列的操作方法,很简单,将元素放到队尾索引,将队尾索引+1,如果入队后队列已满,则重置队尾,队列长度加1,唤醒等待因为无法取到元素二阻塞的线程。注意到,这个队列是一个循环队列
1、offer方法
public boolean offer(E e) { //入队元素判空 checkNotNull(e); final ReentrantLock lock = this.lock; //获取锁 lock.lock(); try { //如果队列已满,返回false if (count == items.length) return false; else { //入队,返回true enqueue(e); return true; } } finally { //释放锁 lock.unlock(); } }
可以看到,offer方法会返回特殊值并且不阻塞。offer方法在队列已满时,返回false,并没有阻塞。入队成功返回true。
2、put方法
public void put(E e) throws InterruptedException { //入队元素判空 checkNotNull(e); final ReentrantLock lock = this.lock; //获取锁 lock.lockInterruptibly(); try { //如果队列已满,则阻塞该线程 while (count == items.length) notFull.await(); //队列未满,则入队 enqueue(e); } finally { lock.unlock(); } }
可以看到,如果队列已满,put方法会阻塞线程。
private E dequeue() { 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是出队的操作方法,很简单,取出队尾索引对应的值,将队尾索引对应的元素置空,如果出队索引到达队列长度,则重置出队索引,队列长度减1,唤醒因入队而阻塞的线程,最后返回对应的元素。
3、poll方法
public E poll() { final ReentrantLock lock = this.lock; //获取锁 lock.lock(); try { //如果队列无元素返回null,不然调用dequeue return (count == 0) ? null : dequeue(); } finally { lock.unlock(); } }
可以看到,poll方法与offer方法类似,也是一个返回特殊值的方法,并且也不阻塞线程。如果队列为空返回null,不然出队头的元素
4、take方法
public E take() throws InterruptedException { final ReentrantLock lock = this.lock; //加锁 lock.lockInterruptibly(); try { //如果队列为空,则阻塞线程 while (count == 0) notEmpty.await(); //队列不为空返回队头元素 return dequeue(); } finally { lock.unlock(); } }
可以看到,take方法和put方法类似,会阻塞线程。
还有等待多长时间的入队和出队方法,因为只是加入了定时器,其余和上述方法没区别,所以我就不说了。
阻塞队列提供了4类方法,如下图所示