白话JUC--Queue体系

早过忘川 提交于 2019-12-24 01:01:25

通过前几篇文章的研究,我们知道在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()方法。

使用时需要注意:

  1. 在队列已经满的情况下添加元素有4种模式:
    - 添加后抛异常
    - 添加后立即返回一个特殊值,可能是false或者null
    - 添加成功前无限期地阻塞
    - 在等待时间段内阻塞,超过后返回
  2. 不接收null元素,尝试add、put、offer null元素的时候,会抛出NullPointerException(因为在poll数据的时候无法判断是元素为null还是队列为空)
  3. 可以指定长度,也可以无界
  4. 是线程安全的
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>
  1. 抽象类继承AbstractCollection实现Queue接口,定义了一些方法的基本实现(可以联想到AQS模板设计模式,定义好抽象类的基本流程,子类实现具体的方法)
  2. 不允许插入null元素
  3. 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这几个实现

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!