原文来自:https://blog.csdn.net/xin_jmail/article/details/26157971
ArrayBlockingQueue是一个由数组支持的有界阻塞队列,也是一个循环队列。此队列按 FIFO(先进先出)原则对元素进行排序。队列的头部 是在队列中存在时间最长的元素,队列的尾部 是在队列中存在时间最短的元素。新元素插入到队列的尾部,队列检索操作则是从队列头部开始获得元素。
这是一个典型的“有界缓存区”,固定大小的数组在其中保持生产者插入的元素和使用者提取的元素。一旦创建了这样的缓存区,就不能再增加其容量。试图向已满队列中放入元素会导致放入操作受阻塞;试图从空队列中检索元素将导致类似阻塞。
ArrayBlockingQueue创建的时候需要指定容量capacity(可以存储的最大的元素个数,因为它不会自动扩容)。其中一个构造方法为:
- 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类中定义的变量有:
-
- private final E[] items;
-
- private int takeIndex;
-
- private int putIndex;
-
- private int count;
-
- private final ReentrantLock lock;
-
- private final Condition notEmpty;
-
- private final Condition notFull;
使用数组items来存储元素,由于是循环队列,使用takeIndex和putIndex来标记put和take的位置。可以看到,该类中只定义了一个锁ReentrantLock,定义两个Condition对象:notEmputy和notFull,分别用来对take和put操作进行所控制。注:本文主要讲解put()和take()操作,其他方法类似。
put(E e)方法的源码如下。进行put操作之前,必须获得锁并进行加锁操作,以保证线程安全性。加锁后,若发现队列已满,则调用notFull.await()方法,如当前线程陷入等待。直到其他线程take走某个元素后,会调用notFull.signal()方法来激活该线程。激活之后,继续下面的插入操作。
-
-
-
-
-
- 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)
- Full.await();
- } catch (InterruptedException ie) {
- Full.signal();
- throw ie;
- }
-
- insert(e);
- } finally {
-
- lock.unlock();
- }
- }
insert(E e) 方法如下:
-
-
-
-
- private void insert(E x) {
- items[putIndex] = x;
-
- putIndex = inc(putIndex);
- ++count;
-
-
- Empty.signal();
- }
-
- **
- * Circularly increment i.
- */
- final int inc(int i) {
-
- return (++i == items.length)? 0 : i;
- }
take()方法代码如下。take操作和put操作相反,故不作详细介绍。
- public E take() throws InterruptedException {
- final ReentrantLock lock = this.lock;
- lock.lockInterruptibly();
- try {
- try {
-
-
- while (count == 0)
- Empty.await();
- } catch (InterruptedException ie) {
- Empty.signal();
- throw ie;
- }
-
- E x = extract();
- return x;
- } finally {
- lock.unlock();
- }
- }
extract() 方法如下:
-
-
-
-
- private E extract() {
- final E[] items = this.items;
- E x = items[takeIndex];
- items[takeIndex] = null;
- takeIndex = inc(takeIndex);
- --count;
- Full.signal();
- return x;
- }
小结:进行put和take操作,共用同一个锁对象。也即是说,put和take无法并行执行!两个Condition来进行通信。