通过前几篇文章的研究,我们知道在JUC包中应用了大量的队列,所以详细了解队列将对我们后续研究并发编程乃至java编程拥有极高的价值
Queue
先从Queue接口开始说起吧
public interface Queue<E> extends Collection<E>
队列接口继承自Collection,说明也是集合类,与List和Set接口平级
定义了队列的基本接口方法
boolean add(E e);
将指定的元素插入此队列(如果立即可行且不会违反容量限制),在成功时返回 true,如果当前没有可用的空间,则抛出 IllegalStateException。
boolean offer(E e);
将指定的元素插入此队列(如果立即可行且不会违反容量限制),当使用有容量限制的队列时,此方法通常要优于 add(E),后者可能无法插入元素,而只是抛出一个异常。
E remove();
获取并移除此队列的头。如果队列为空,则抛出异常
E poll();
获取并移除此队列的头,如果此队列为空,则返回 null。
E element();
获取,但是不移除此队列的头。此方法与 peek 唯一的不同在于:此队列为空时将抛出一个异常。
E peek();
获取但不移除此队列的头;如果此队列为空,则返回 null。
Queue接口有两个重要的子接口
- BlockingQueue
- Deque
BlockingQueue
public interface BlockingQueue<E> extends Queue<E>
阻塞队列接口,BlockingQueue在Queue接口的基础上加了几个方法:put(e)和take(),并且重载了Queue接口offer(e)和poll()方法。
使用时需要注意:
- 在队列已经满的情况下添加元素有4种模式:
- 添加后抛异常
- 添加后立即返回一个特殊值,可能是false或者null
- 添加成功前无限期地阻塞
- 在等待时间段内阻塞,超过后返回 - 不接收null元素,尝试add、put、offer null元素的时候,会抛出NullPointerException(因为在poll数据的时候无法判断是元素为null还是队列为空)
- 可以指定长度,也可以无界
- 是线程安全的
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();
返回在无阻塞的理想情况下(不存在内存或资源约束)此队列能接受的附加元素数量;如果没有内部限制,则返回Integer.MAX_VALUE。
public boolean contains(Object o);
如果此队列包含指定元素,则返回 true
int drainTo(Collection<? super E> c);
移除此队列中所有可用的元素,并将它们添加到给定 collection 中
int drainTo(Collection<? super E> c, int maxElements);
最多从此队列中移除给定数量的可用元素,并将这些元素添加到给定 collection 中
BlockingQueue相比Queue在方法声明中多了两个核心方法:
- take:获取并移除此队列的头部,在元素变得可用之前一直等待
- put:将指定元素插入此队列中,将等待可用的空间
Deque
public interface Deque<E> extends Queue<E>
Deque双端队列是一种特殊的队列。支持在两端插入和移除元素(同样支持顺序插入顺序移除FIFO)。名称Deque是“double ended queue(双端队列)”的缩写。大多数Deque实现对于它们能够包含的元素数没有固定限制,但此接口既支持有容量限制的双端队列,也支持没有固定大小限制的双端队列。
void addFirst(E e);
boolean offerFirst(E e);
E removeFirst();
E pollFirst();
E getFirst();
E peekFirst();
以上方法都是对队列头进行操作的方法,我们通过了解queue接口中的方法可能对这里面的方法也有所了解了,Deque也定义了两种方法:
- 一种是在满队列或者空队列的操作元素时,会抛出异常
- 另一种在满队列或者空队列的操作元素时,会返回 null 或是 false
void addFirst(E e);
E removeFirst();
E getFirst();
以上方法操作头元素的时候会抛出异常
boolean offerFirst(E e);
E pollFirst();
E peekFirst();
以上方法会返回特殊值,可能是null或者false
void addLast(E e);
boolean offerLast(E e);
E removeLast();
E pollLast();
E getLast();
E peekLast();
以上方法都是对队列尾进行操作的方法
BlockingDeque
public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E>
通过继承关系我们可以发现BlockingDeque既继承了BlockingQueue接口又继承了Deque,所以具有阻塞和双端队列的特性
LinkedBlockingDeque
BlockingDeque的实现类
ArrayDeque&ConcurrentLinkedDeque&LinkedList
这三个类都是对Deque的实现类
AbstractQueue
public abstract class AbstractQueue<E> extends AbstractCollection<E> implements Queue<E>
- 抽象类继承AbstractCollection实现Queue接口,定义了一些方法的基本实现(可以联想到AQS模板设计模式,定义好抽象类的基本流程,子类实现具体的方法)
- 不允许插入null元素
- add、remove 和 element 方法分别基于 offer、poll 和 peek 方法,但是它们通过抛出异常而不是返回 false 或 null 来指示失败
public boolean add(E e) {
if (offer(e))
return true;
else
throw new IllegalStateException("Queue full");
}
将指定的元素插入到此队列中(如果立即可行且不会违反容量限制),在成功时返回true,如果当前没有可用空间,则抛出IllegalStateException
public E remove() {
E x = poll();
if (x != null)
return x;
else
throw new NoSuchElementException();
}
获取并移除此队列的头,在成功时返回获取到的元素,如果获取到为空,则抛出NoSuchElementException
public E element() {
E x = peek();
if (x != null)
return x;
else
throw new NoSuchElementException();
}
获取但不移除此队列的头,在成功时返回获取到的元素,如果获取到为空,则抛出NoSuchElementException
public void clear() {
while (poll() != null)
;
}
移除此队列中的所有元素
public boolean addAll(Collection<? extends E> c) {
if (c == null)
throw new NullPointerException();
if (c == this)
throw new IllegalArgumentException();
boolean modified = false;
for (E e : c)
if (add(e))
modified = true;
return modified;
}
将指定集合中的所有元素都添加到此队列中,判断集合是否为空,判断是否为当前集合,之后循环遍历集合,添加到队列中
通过对上面方法的分析,可以看出AbstractQueue定义了一些公共的增删取得方法,具体如何实现还需要依赖子类的offer、poll、peek方法
AbstractQueue实现类
这里先简单的介绍一下,后续会对每种集合做一个分析
ArrayBlockingQueue
一个内部由数组支持的有界队列。初始化时必须指定队列的容量。它是基于数组的阻塞循环队列,此队列按 FIFO(先进先出)原则对元素进行排序
LinkedBlockingQueue
一个内部由链接节点支持的可选有界队列。初始化时不需要指定队列的容量,默认是Integer.MAX_VALUE,也可以看成容量无限大。此队列按 FIFO(先进先出)排序元素
PriorityBlockingQueue
一个内部由优先级堆支持的无界优先级队列。PriorityBlockingQueue是对 PriorityQueue的再次包装,队列中的元素按优先级顺序被移除,必须自己实现元素的compareTo方法才能达到目的
DelayQueue
一个内部由优先级堆支持的、基于时间的调度队列。队列中存放Delayed元素,只有在延迟期满后才能从队列中提取元素。当一个元素的getDelay()方法返回值小于等于0时才能从队列中poll中元素,否则poll()方法会返回null,只有达到失效时间才能够被取出来
SynchronousQueue
此队列中只能有一个元素,取一个塞一个
ConcurrentLinkedQueue
前面提到的queue都是非线程安全的,ConcurrentLinkedQueue是线程安全的队列,头和尾节点都是用volatile关键字保证数据可见性的,其次通过锁来实现阻塞的功能
最后看一下整体的继承关系后会有一个整体的概念
可以看到AbstractQueue是对AbstractCollection的扩展,AbstractCollection下面又有ArrayDeque、ConcurrentLinkedDeque、LinkedList这几个实现