通过前几篇文章的研究,我们知道在JUC包中应用了大量的队列,所以详细了解队列将对我们后续研究并发编程乃至java编程拥有极高的价值
先从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接口有两个重要的子接口
public interface BlockingQueue<E> extends Queue<E>
阻塞队列接口,BlockingQueue在Queue接口的基础上加了几个方法:put(e)和take(),并且重载了Queue接口offer(e)和poll()方法。
使用时需要注意:
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在方法声明中多了两个核心方法:
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也定义了两种方法:
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();
以上方法都是对队列尾进行操作的方法
public interface BlockingDeque<E> extends BlockingQueue<E>, Deque<E>
通过继承关系我们可以发现BlockingDeque既继承了BlockingQueue接口又继承了Deque,所以具有阻塞和双端队列的特性
BlockingDeque的实现类
这三个类都是对Deque的实现类
public abstract class AbstractQueue<E> extends AbstractCollection<E> implements Queue<E>
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方法
这里先简单的介绍一下,后续会对每种集合做一个分析
一个内部由数组支持的有界队列。初始化时必须指定队列的容量。它是基于数组的阻塞循环队列,此队列按 FIFO(先进先出)原则对元素进行排序
一个内部由链接节点支持的可选有界队列。初始化时不需要指定队列的容量,默认是Integer.MAX_VALUE,也可以看成容量无限大。此队列按 FIFO(先进先出)排序元素
一个内部由优先级堆支持的无界优先级队列。PriorityBlockingQueue是对 PriorityQueue的再次包装,队列中的元素按优先级顺序被移除,必须自己实现元素的compareTo方法才能达到目的
一个内部由优先级堆支持的、基于时间的调度队列。队列中存放Delayed元素,只有在延迟期满后才能从队列中提取元素。当一个元素的getDelay()方法返回值小于等于0时才能从队列中poll中元素,否则poll()方法会返回null,只有达到失效时间才能够被取出来
此队列中只能有一个元素,取一个塞一个
前面提到的queue都是非线程安全的,ConcurrentLinkedQueue是线程安全的队列,头和尾节点都是用volatile关键字保证数据可见性的,其次通过锁来实现阻塞的功能
可以看到AbstractQueue是对AbstractCollection的扩展,AbstractCollection下面又有ArrayDeque、ConcurrentLinkedDeque、LinkedList这几个实现