Java中竟有18种队列?45张图!安排

今天我们来盘点一下Java中的Queue家族,总共涉及到18种Queue。这篇恐怕是市面上最全最细讲解Queue的。

本篇主要内容如下:

本篇主要内容

帮你总结好的阻塞队列:

18种Queue总结

一、Queue自我介绍

队列原理图

1.1 Queue自我介绍

hi,大家好,我的英文名叫Queue,中文名叫队列,无论现实生活中还是计算机的世界中,我都是一个很重要的角色哦~

我是一种数据结构,大家可以把我想象成一个数组,元素从我的一头进入、从另外一头出去,称为FIFO原则(先进先出原则)。

我还有两个亲兄弟:List(列表)、Set(集),他们都是Collection的儿子,我还有一个远房亲戚:Map(映射)。他们都是java.util包这个大家庭的成员哦~

1.2 现实生活中的场景

  • 海底捞排号等位(先排号的优先进餐厅)

  • 邮政员寄送信件(信箱是队列)

1.3 计算机世界中的场景

  • 消息队列 RabbitMQ

  • UDP协议(接收端将消息存放在队列中,从队列中读取数据)

二、高屋建瓴,纵览全局

18种队列分为三大类: 接口、抽象类、普通类。

弄清楚下面的继承实现关系对后面理解18种队列有很大帮助。

18个Queue的继承实现关系图
  • Queue接口继承 Collection接口,Collection接口继承  Iterable接口

  • BlockingQueue接口、Deque接口 继承 Queue接口

  • AbstractQueue抽象类实现 Queue接口

  • BlockingDeque接口、TransferQueue接口继承 BlockingQueue接口

  • BlockingDeque接口继承Deque接口

  • LinkedBlockingDeque实现 BlockingDeque接口

  • LinkedTransferQueue类接口实现 TransferQueue接口

  • LinkedList类、ArrayDeque类、ConcurrentLinkedDeque实现Deque接口

  • ArrayBlockingQueue类、LinkendBlockingQueue类、LinkedBlockingDeque类、LinkedTransferQueue类、SynchronousQueue类、PriorityBlockQueue类、DelayQueue类继承AbstractQueue抽象类和实现了BlockingQueue接口

  • PriorityQueue类和ConcurrentLinkedQueue继承AbstractQueue抽象类

注意:

  • Deque:全称Double-Ended queue,表示双端队列。

  • 类实现接口,用implements

  • 接口继承接口,用 extends

  • 类继承类,用extends

三、万物归宗Queue接口

2.1 深入理解Queue接口的本质

  • Queue接口是一种Collection,被设计用于处理之前临时保存在某处的元素。

  • 除了基本的Collection操作之外,队列还提供了额外的插入、提取和检查操作。每一种操作都有两种形式:如果操作失败,则抛出一个异常;如果操作失败,则返回一个特殊值(null或false,取决于是什么操作)。

  • 队列通常是以FIFO(先进先出)的方式排序元素,但是这不是必须的。

  • 只有优先级队列可以根据提供的比较器对元素进行排序或者是采用正常的排序。无论怎么排序,队列的头将通过调用remove()或poll()方法进行移除。在FIFO队列种,所有新的元素被插入到队尾。其他种类的队列可能使用不同的布局来存放元素。

  • 每个Queue必须指定排序属性。

2.2 Queue接口的核心方法

总共有3组方法,每一组方法对应两个方法。如下图所示:

Queue的核心方法

动作抛异常返回特殊值
Insertadd(e)offer(e)
Removeremove()poll
Examineelement()peek()

  • (1)比如添加(Insert)元素的动作,会有两种方式:add(e)offer(e)。如果调用add(e)方法时,添加失败,则会抛异常,而如果调用的是offer(e)方法失败时,则会返回false。offer方法用于异常是正常的情况下使用,比如在有界队列中,优先使用offer方法。假如队列满了,不能添加元素,offer方法返回false,这样我们就知道是队列满了,而不是去handle运行时抛出的异常。

  • (2)同理,移除(Remove)元素的动作,队列为空时,remove方法抛异常,而poll返回null。如果移除头部的元素成功,则返回移除的元素。

  • (3)同理,检测(Examine)元素的动作,返回头部元素(最开始加入的元素),但不删除元素, 如果队列为空,则element()方法抛异常,而peek()返回false。

  • (4)Queue接口没有定义阻塞队列的方法,这些方法在BlockQueue接口中定义了。

  • (5)Queue实现类通常不允许插入null元素,尽管一些实现类比如LinkedList不禁止插入null,但是还是不建议插入null,因为null也被用在poll方法的特殊返回值,以说明队列不包含元素。

四、双端可用Deque接口

4.1 深入理解Deque接口的原理

双端队列Deque

(1)Deque概念: 支持两端元素插入和移除的线性集合。名称deque是双端队列的缩写,通常发音为deck。大多数实现Deque的类,对它们包含的元素的数量没有固定的限制的,支持有界和无界。

(2)Deque方法说明:

Deque方法

说明:

  • 该列表包含包含访问deque两端元素的方法,提供了插入,移除和检查元素的方法。

  • 这些方法种的每一种都存在两种形式:如果操作失败,则会抛出异常,另一种方法返回一个特殊值(null或false,取决于具体操作)。

  • 插入操作的后一种形式专门设计用于容量限制的Deque实现,大多数实现中,插入操作不能失败,所以可以用插入操作的后一种形式。

  • Deque接口扩展了Queue接口,当使用deque作为队列时,作为FIFO。元素将添加到deque的末尾,并从头开始删除。

  • 作为FIFO时等价于Queue的方法如下表所示:

比如Queue的add方法和Deque的addLast方法等价。

  • Deque也可以用作LIFO(后进先出)栈,这个接口优于传统的Stack类。当作为栈使用时,元素被push到deque队列的头,而pop也是从队列的头pop出来。

  • Stack(栈)的方法正好等同于Deque的如下方法:

注意:peek方法不论是作为栈还是队列,都是从队列的检测队列的头,返回最先加入的元素。比如第一次put 100,第二次put 200,则peek返回的是100。如下图所示:

示例代码

4.1 哪些类实现了Deque接口

  • LinkedList类

  • ArrayDeque类

  • ConcurrentLinkedDeque类

  • LinkedBlockingDeque类

4.2 哪些类继承了Deque接口

  • BlockingDeque接口

五、队列骨架AbstractQueue抽象类

5.1  深入理解AbstractQueue抽象类

AbstractQueue是一个抽象类,继承了Queue接口,提供了一些Queue操作的骨架实现。

AbstractQueue的方法

方法add、remove、element方法基于offer、poll和peek。也就是说如果不能正常操作,则抛出异常。我们来看下AbstactQueue是怎么做到的。

  • AbstractQueue的add方法

public boolean add(E e) {if (offer(e))return true;elsethrow new IllegalStateException("Queue full");
}
  • AbstractQueue的remove方法

public E remove() {E x = poll();if (x != null)return x;elsethrow new NoSuchElementException();
}
  • AbstractQueue的element方法

public E element() {E x = peek();if (x != null)return x;elsethrow new NoSuchElementException();
}

注意:

  • 如果继承AbstractQueue抽象类则必须保证offer方法不允许null值插入。

5.2 哪些类继承了AbstractQueue抽象类

  • ArrayBlockingQueue类、LinkendBlockingQueue类、LinkedBlockingDeque类、LinkedTransferQueue类、SynchronousQueue类、PriorityBlockQueue类、DelayQueue类继承AbstractQueue抽象类

  • PriorityQueue类和ConcurrentLinkedQueue继承AbstractQueue抽象类

六、阻塞缓冲BlockingQueue接口

6.1 宏观来看BlockingQueue(阻塞队列)

  • BlockQueue满了,PUT操作被阻塞

阻塞队列满了的情况
  • BlockQueue为空,Take操作被阻塞

阻塞队列为空的情况

(1)BlockingQueue(阻塞队列)也是一种队列,支持阻塞的插入和移除方法。

(3)阻塞的插入:当队列满时,队列会阻塞插入元素的线程,直到队列不满。

(4)阻塞的移除:当队列为空,获取元素的线程会等待队列变为非空。

(5)应用场景:生产者和消费者,生产者线程向队列里添加元素,消费者线程从队列里移除元素,阻塞队列时获取和存放元素的容器。

(6)为什么要用阻塞队列:生产者生产和消费者消费的速率不一样,需要用队列来解决速率差问题,当队列满了或空的时候,则需要阻塞生产或消费动作来解决队列满或空的问题。

6.2 案例解析

线程A往阻塞队列(Blocking Queue)中添加元素,而线程B从阻塞队列中移除元素。

  • 当阻塞队列为空的时候 (一个元素都没有),则从队列中获取元素的操作将会被阻塞。

    • 生活中的案例:去海底捞吃火锅的时候,早上8点没人来吃火锅,所以需要等客人过来。

    • 翻译成线程:现在没有元素需要添加,而且阻塞队列为空,所以线程B不需要从队列中拿元素出来,所以线程B获取元素的操作被阻塞了。

  • 当阻塞队列满了的时候 (所有位置都放有元素),则从队列中添加元素的操作将会被阻塞。

    • 生活中的案例:去海底捞吃火锅的时候,人太多了,需要排号,等其他桌空出来了才能进去。

    • 翻译成线程:线程A往阻塞队列中添加元素,将队列填满了,线程B现在正在忙,无法拿出队列中的元素,所以阻塞队列没有地方再放元素了,这个时候线程A添加元素的操作就被阻塞了

6.3 操刀BlockingQueue接口

BlockingQueue接口的10个核心方法:

继承的方法

10个核心方法总结如下:

BlockingQueue接口的10个核心方法

有三大类操作:插入、移除、检查。

  • 插入有四种方法: add、offer、put、offer超时版。

    • IllegalStateException - 队列满了

    • ClassCastException - 添加的元素类型不匹配

    • NullPointerException - 添加的元素为null

    • IllegalArgumentException - 添加的元素某些属性不匹配

    • add方法特别之处用于添加失败时抛出异常,共有四种异常:

    • offer方法特别之处用于添加失败时只返回false

    • put方法特别之处用于当阻塞队列满时,生产者如果往队列里put元素,则队列会一直阻塞生产者线程,直到队列可用或者响应中断退出

    • offer超时方法特别之处用于当阻塞队列满时,生产者如果往队列里面插入元素,队列会阻塞生产者线程一段时间,如果超过了指定时间,生产者线程会退出,并返回false。

  • 移除有四种方法: remove、poll、take、poll超时版

    • NoSuchElementException - 如果这个队列是空的

    • remove方法特别之处用于移除失败时抛出异常

    • poll方法特别之处用于移除失败时返回null

    • take方法特别之处用于当阻塞队列为空时,消费者线程如果从队列里面移除元素,则队列会一直阻塞消费者线程,直到队列不为空

    • poll超时方法特别之处用于当阻塞队列空时,消费者如果从队列里面删除元素,则队列会一直阻塞消费者线程,如果超过了指定时间,消费者线程会退出,并返回null

  • 检查有两种方法: element、peek

    • element方法用于检测头部元素的存在性,如果队列为空,则抛出异常,否则返回头部元素。

    • peek方法用于检测头部元素的存在性,如果队列为空,返回特殊值null,否则返回头部的元素。

6.4 BlockingQueue通过什么来阻塞插入和移除的?

  • 当往队列里插入一个元素时,如果队列不可用,那么阻塞生产者主要通过LockSupport. park(this)来实现。

  • park这个方法会阻塞当前线程,只有以下4种情况中的一种发生时,该方法才会返回。

    • 与park对应的unpark执行或已经执行时。“已经执行”是指unpark先执行,然后再执行park的情况。

    • 线程被中断时。

    • 等待完time参数指定的毫秒数时。

    • 异常现象发生时,这个异常现象没有任何原因。

6.5 哪些类继承了BlockingQueue接口?

  • BlockingDeque接口 - 双端阻塞队列

  • TransferQueue接口 - 传输队列

6.6 哪些类实现了BlockingQueue接口?

  • ArrayBlockingQueue类 - 由数组构成的有界阻塞队列

  • LinkedBlockingQueue类 - 由链表构成的有界阻塞队列,界限默认大小为Integer.MAX_Value(2^31-1),值非常大,相当于无界。

  • LinkedBlockingDeque类 - 由链表构成的双向阻塞队列

  • LinkedTransferQueue类 - 由链表构成的无界阻塞队列

  • SynchronousQueue类 - 不存储元素的阻塞队列,只有一个元素进行数据传递。

  • LinkedTransferQueue类 - 由链表构成的无界阻塞TransferQueue队列

  • DelayQueue类 - 使用优先级队列实现的延迟无界阻塞队列

6.6 BlockingQueue接口继承了哪些接口

  • BlockingQueue接口继承了Queue接口,可作为队列使用

七、双端阻塞BlockingDeque接口

7.1 从原理图上理解BlockDeque

  • BlockQueue满了,两端的Take操作被阻塞

BlockingDeque满了
  • BlockQueue为空,两端的Take操作被阻塞

BlockQueue为空

7.2 BlockingDeque接口方法

是阻塞队列BlockingQueue和双向队列Deque接口的结合。有如下方法:

BlockingDeque接口方法

示例:

尝试执行以下方法:

LinkedBlockingDeque queue = new LinkedBlockingDeque();
queue.addFirst("test1");
queue.addFirst(300);
queue.addLast("400");

最后队列中的元素顺序如下:

300, test1, 400。

先添加了test1放到队列的头部,然后在头部的前面放入300,所以300在最前面,成为头部,然后将400放入队列的尾部,所以最后的结果是300, test1, 400。

队列种的元素

7.3 BlockDeque和BlockQueue的对等方法

BlockDeque和BlockQueue的对等方法

7.4 BlockingDeque的特点

  • 线程安全。

  • 不允许使用null元素。

  • 无界和有界都可以。

7.5 BlockingDeque接口继承了哪些接口?

  • Queue接口,具有队列的功能

  • Deque接口,具有双端队列的功能

  • BlockingQueue接口,可作为阻塞队列使用

7.6 哪些类实现了BlockDeque接口?

  • LinkedBlockingDeque

八、使命必达TransferQueue接口

8.1 Transfer怎么理解?

如果有消费者正在获取元素,则将队列中的元素传递给消费者。如果没有消费者,则等待消费者消费。我把它称作使命必达队列,必须将任务完成才能返回。

8.2 生活中的案例

  • 针对TransferQueue的transfer方法

    • 圆通快递员要将小明的2个快递送货到门,韵达快递员也想将小明的2个快递送货到门。小明一次只能拿一个,快递员必须等小明拿了一个后,才能继续给第二个。

  • 针对TransferQueue的tryTransfer方法

    • 圆通快递员要将小明的2个快递送货到门,韵达快递员也想将小明的2个快递送货到门。发现小明不在家,就把快递直接放到菜鸟驿站了。

  • 针对TransferQueue的tryTransfer超时方法

    • 圆通快递员要将小明的2个快递送货到门,韵达快递员也想将小明的2个快递送货到门。发现小明不在家,于是先等了5分钟,发现小明还没有回来,就把快递直接放到菜鸟驿站了。

8.3 TransferQueue的原理解析

  • transfer(E e)

    原理如下图所示:

    transfer方法的原理
    • 原理图解释:生产者线程Producer Thread尝试将元素B传给消费者线程,如果没有消费者线程,则将元素B放到尾节点。并且生产者线程等待元素B被消费。当元素B被消费后,生产者线程返回。

    • 如果当前有消费者正在等待接收元素(消费者通过take方法或超时限制的poll方法时),transfer方法可以把生产者传入的元素立刻transfer(传输)给消费者。

    • 如果没有消费者等待接收元素,transfer方法会将元素放在队列的tail(尾)节点,并等到该元素被消费者消费了才返回。

  • tryTransfer(E e)

    • 试探生产者传入的元素是否能直接传给消费者。

    • 如果没有消费者等待接收元素,则返回false。

    • 和transfer方法的区别是,无论消费者是否接收,方法立即返回。

  • tryTransfer(E e, long timeout, TimeUnit unit)

    • 带有时间限制的tryTransfer方法。

    • 试图把生产者传入的元素直接传给消费者。

    • 如果没有消费者消费该元素则等待指定的时间再返回。

    • 如果超时了还没有消费元素,则返回false。

    • 如果在超时时间内消费了元素,则返回true。

  • getWaitingConsumerCount()

    • 获取通过BlockingQueue.take()方法或超时限制poll方法等待接受元素的消费者数量。近似值。

    • 返回等待接收元素的消费者数量。

  • hasWaitingConsumer()

    • 获取是否有通过BlockingQueue.tabke()方法或超时限制poll方法等待接受元素的消费者。

    • 返回true则表示至少有一个等待消费者。

8.3 TransferQueue接口继承了哪些接口?

  • BlockingQueue接口,可作为阻塞队列使用

  • Queue接口,可作为队列使用

8.4 哪些类实现了TransferQueue接口?

  • LinkedTranferQueue接口

九、优先由你PriorityQueue类

9.1 理解PriorityQueue类

  • 本应该按照升序排序

本应该按照升序排序
  • 按照倒叙排序

按照自定义优先级排序
  • PriorityQueue是一个支持优先级的无界阻塞队列。

  • 默认自然顺序升序排序。

  • 可以通过构造参数Comparator来对元素进行排序。

public PriorityQueue(Comparator<? super E> comparator) {this(DEFAULT_INITIAL_CAPACITY, comparator);
}
  • 自定义实现comapreTo()方法来指定元素排序规则。

public Comparator<? super E> comparator() {return comparator;
}
  • 不允许插入null元素。

  • 实现PriorityQueue接口的类,不保证线程安全,除非是PriorityBlockingQueue。

  • PriorityQueue的迭代器不能保证以任何特定顺序遍历元素,如果需要有序遍历,请考虑使用Arrays.sort(pq.toArray)

  • 进列(offeradd)和出列( pollremove())的时间复杂度O(log(n))。

  • remove(Object) 和 contains(Object)的算法时间复杂度O(n)。

  • peek、element、size的算法时间复杂度为O(1)。

9.2 PriorityQueue类继承了哪些类?

  • AbstractQueue抽象类,具有队列的功能

9.2 PriorityQueue类实现了哪些接口?

  • Queue接口,可作为队列使用。

十、双向链表LinkedList类

10.1 LinkedList的结构

  • LinkedList实现了List和Deque接口,所以是一种双链表结构,可以当作堆栈、队列、双向队列使用。

  • 一个双向列表的每一个元素都有三个整数值:元素、向后的节点链接、向前的节点链接

LinkedList的结构

我们来看下节点类Node

private static class Node<E> {E item; //元素Node<E> next; //向后的节点链接Node<E> prev; //向前的节点链接Node(Node<E> prev, E element, Node<E> next) {this.item = element;this.next = next;this.prev = prev;}
}

10.2 与ArrayList的区别

  • 1.LinkedList的增加和删除效率相对较高,而查找和修改的效率相对较低。

  • 2.以下情况建议使用ArrayList

    • 频繁访问列表中的一个元素。

    • 只在列表的首尾添加元素。

  • 3.以下情况建议使用LinkedList

    • 频繁地在列表开头、中间、末尾添加和删除元素。

    • 需要通过循环迭代来访问列表中的元素。

10.3 LinkedList不是线程安全的

LinkedList不是线程安全的,所以可以使用如下方式保证线程安全。

List list = Collections.synchronizedList(new LinkedList<>());

10.4 LinkedList的家庭成员关系

  • LinkedList 继承了 AbstractSequentialList 类。

  • LinkedList 实现了 Queue 接口,可作为队列使用。

  • LinkedList 继承了 AbstractQueue抽象类,具有队列的功能。

  • LinkedList 实现了 List 接口,可进行列表的相关操作。

  • LinkedList 实现了 Deque 接口,可作为双向队列使用。

  • LinkedList 实现了 Cloneable 接口,可实现克隆。

  • LinkedList 实现了 java.io.Serializable 接口,即可支持序列化,能通过序列化去传输。

十一、并发安全ConcurrentLinkedQueue类

11.1 理解ConcurrentLinkedQueue

ConcurrentLinkedQueue原理
  • ConcurrentLinked是由链表结构组成的线程安全的先进先出无界队列。

  • 当多线程要共享访问集合时,ConcurrentLinkedQueue是一个比较好的选择。

  • 不允许插入null元素

  • 支持非阻塞地访问并发安全的队列,不会抛出ConcurrentModifiationException异常。

  • size方法不是准确的,因为在统计集合的时候,队列可能正在添加元素,导致统计不准。

  • 批量操作addAll、removeAll、retainAll、containsAll、equals和toArray不保证原子性(操作不可分割)

  • 添加元素happen-before其他线程移除元素。

  • 用法如下:

ConcurrentLinkedQueue queue = new ConcurrentLinkedQueue();
BuildingBlockWithName buildingBlock = new BuildingBlockWithName("三角形", "A");
concurrentLinkedQueue.add(buildingBlock);

11.2 ConcurrentLinkedQueue类继承了哪些类?

  • AbstractQueue抽象类,具有队列的功能

11.3 ConcurrentLinkedQueue类实现了哪些接口?

  • Queue接口,可作为队列使用

十二、双向数组ArrayDeque类

ArrayDeque原理图

12.1 理解ArrayDeque

  • 由数组组成的双端队列。

  • 没有容量限制,根据需要扩容。

  • 不是线程安全的。

  • 禁止插入null元素。

  • 当用作栈时,比栈速度快,当用作队列时,速度比LinkList快。

  • 大部分方法的算法时间复杂度为O(1)。

  • remove、removeFirstOccurrence、removeLastOccurrence、contains、remove 和批量操作的算法时间复杂度O(n)

12.2 使用方法

创建一个ArrayDeque,往arrayDeque队尾添加元素。

ArrayDeque arrayDeque = new ArrayDeque();
for (int i = 0; i < 50; i++) {arrayDeque.add(buildingBlock); // add方法等价于addLast方法
}

12.3 ArrayDeque实现了哪些接口

  • Deque接口 - 可用于双端队列

十三、双向并发ConcurrentLinkedDeque类

13.1 理解ConcurrentLinkedDeque类

ConcurrentLinkedDeque原理图
  • 由链表结构组成的双向无界阻塞队列

  • 插入、删除和访问操作可以并发进行,线程安全的类

  • 不允许插入null元素

  • 在并发场景下,计算队列的大小是不准确的,因为计算时,可能有元素加入队列。

  • 批量操作addAll、removeAll、retainAll、containsAll、equals和toArray不保证原子性(操作不可分割)

13.2 ConcurrentLinkedDeque使用示例

创建两个积木:三角形、四边形,然后添加到队列:

BuildingBlockWithName buildingBlock1 = new BuildingBlockWithName("三角形", "A");
BuildingBlockWithName buildingBlock2 = new BuildingBlockWithName("四边形", "B");
ConcurrentLinkedDeque concurrentLinkedDeque = new ConcurrentLinkedDeque();
concurrentLinkedDeque.addFirst(buildingBlock1);
concurrentLinkedDeque.addLast(buildingBlock2);
//结果:顺序:三角形、四边形

13.3 ConcurrentLinkedDeque实现了哪些接口

  • Deque接口 - 可用于双端队列

十四、数组阻塞ArrayBlockingQueue类

14.1 理解ArrayBlockingQueue

ArrayBlockingQueuey原理图
  • ArrayBlockingQueue是一个用数组实现的有界阻塞队列。

  • 队列慢时插入操作被阻塞,队列空时,移除操作被阻塞。

  • 按照先进先出(FIFO)原则对元素进行排序。

  • 默认不保证线程公平的访问队列。

  • 公平访问队列:按照阻塞的先后顺序访问队列,即先阻塞的线程先访问队列。

  • 非公平性是对先等待的线程是非公平的,当队列可用时,阻塞的线程都可以争夺访问队列的资格。有可能先阻塞的线程最后才访问访问队列。

  • 公平性会降低吞吐量。

14.2 ArrayBlockingQueue使用示例

创建两个积木:三角形、四边形,然后添加到队列:

BuildingBlockWithName buildingBlock1 = new BuildingBlockWithName("三角形", "A");
BuildingBlockWithName buildingBlock2 = new BuildingBlockWithName("四边形", "B");
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(100, true);
arrayBlockingQueue.add(buildingBlock1);
arrayBlockingQueue.add(buildingBlock2);

14.3 ArrayBlockQueue实现了哪些接口

  • Deque接口 - 可用于双端队列

十五、链表阻塞LinkedBlockingQueue类

15.1 理解LinkedBlockingQueue

LinkedBlockingQueue原理
  • LinkedBlockingQueue具有单链表和有界阻塞队列的功能。

  • 队列慢时插入操作被阻塞,队列空时,移除操作被阻塞。

  • 默认和最大长度为Integer.MAX_VALUE,相当于无界(值非常大:2^31-1)。

15.2 LinkedBlockingQueue使用示例

LinkedList linkedList1 = new LinkedList();
linkedList1.add("A");
linkedList1.add("B");
linkedList1.add("C");

15.3 LinkedBlockingQueue的应用场景

  • 吞吐量通常要高于ArrayBlockingQueue。创建线程池时,参数runnableTaskQueue(任务队列),用于保存等待执行的任务的阻塞队列可以选择LinkedBlockingQueue。静态工厂方法Executors.newFixedThreadPool()使用了这个队列。

15.4 LinkedBlockingQueue实现了哪些接口

  • LinkedBlockingQueue继承了 BlockingQueue类,可作为阻塞队列使用

  • LinkedBlockingQueue继承了 AbstractQueue抽象类,具有队列的功能。

  • LinkedBlockingQueue实现了 java.io.Serializable 接口,即可支持序列化,能通过序列化去传输。

十六、双向阻塞LinkedBlockingDeque类

16.1 理解LinkedBlockingDeque类

LinkedBlockingDeque原理图
  • 由链LinkedBlockingDeque = 阻塞队列+链表+双端访问

  • 线程安全。

  • 多线程同时入队时,因多了一端访问入口,所以减少了一半的竞争。

  • 默认容量大小为Integer.MAX_VALUE。可指定容量大小。

16.2 LinkedBlockingDeque的应用场景

LinkedBlockingDeque可以用在“工作窃取“模式中。

工作窃取算法:某个线程比较空闲,从其他线程的工作队列中的队尾窃取任务来帮忙执行。

16.3 LinkedBlockingDeque实现了哪些接口

  • LinkedBlockingDeque继承了 BlockingDeque类,可作为阻塞队列使用

  • LinkedBlockingDeque继承了 AbstractQueue抽象类,具有队列的功能。

  • LinkedBlockingDeque实现了 java.io.Serializable 接口,即可支持序列化,能通过序列化去传输。

十七、链表阻塞LinkedTransferQueue类

17.1 理解LinkedTransferQueue类

LinkedTransferQueue原理图

LinkedTransferQueue = 阻塞队列+链表结构+TransferQueue

之前我们讲“使命必达TransferQueue接口时已经介绍过了TransferQueue接口 ,所以LinkedTransferQueue接口跟它相似,只是加入了阻塞插入和移除的功能,以及结构是链表结构。

之前的TransferQueue也讲到了3个案例来说明TransferQueue的原理,大家可以回看TransferQueue。

17.2 LinkedTransferQueue接口比其他阻塞队列多了5个方法

  • transfer(E e)

  • tryTransfer(E e)

  • tryTransfer(E e, long timeout, TimeUnit unit)

  • getWaitingConsumerCount()

  • hasWaitingConsumer()

17.3 LinkedTransferQueue代码示例

  • 创建一个LinkedTransferQueue,生产者1 依次往队列中添加 A、B、C

生产者1 依次往队列中添加 A、B、C
  • 生产者2 依次往队列中添加 D、E、F

生产者2 依次往队列中添加 D、E、F
  • 消费者依次从队列首部开始消费元素,每次消费前,先sleep 2s,来演示transfer方法是否进行了等待。

消费者消费元素
  • 运行结果

生产者1     transfer A 
生产者2     transfer D 2s后...消费者      take A
生产者1     put B 2s后...消费者      take D
生产者2     transfer E 2s后...消费者      take B
生产者1     transfer C 
  • 代码执行结果分析

(1)首先生产者线程1和2 调用transfer方法来传输A和D,发现没有消费者线程接收,所以被阻塞。

(2)消费者线程过了2s后将A拿走了,然后生产者1 被释放继续执行,传输元素B,发现又没有消费者消费,所以进行了等待。

(3)消费者线程过了2s后,将排在队列首部的D元素拿走,生产者2继续往下执行,传输元素E,发现没有消费者,所以进行了等待。

(4)消费者线程过了2s后,将排在队列首部的B元素拿走,生产者1传输C元素,等待消费者拿走。

(5)消费者不再消费了,所以生产者1和生产者2都被阻塞了,元素C和,元素E都没有被拿走,而且生产者2的F元素还没有开始传输,因为在等待元素D被拿走。

(6)看下队列里面确实有C和E元素,而且E排在队列的首部。

队列里面的元素

17.4 LinkedTransferQueue实现了哪些接口

  • LinkedBlockingDeque继承了 BlockingQeque类,可作为阻塞队列使用

  • LinkedBlockingDeque继承了 AbstractQueue抽象类,具有队列的功能。

十八、传球好手SynchronousQueue类

18.1 理解SynchronousQueue类

SynchronousQueue原理图
  • 我称SynchronousQueue为”传球好手“。想象一下这个场景:小明抱着一个篮球想传给小花,如果小花没有将球拿走,则小明是不能再拿其他球的。

  • SynchronousQueue负责把生产者产生的数据传递给消费者线程。

  • SynchronousQueue本身不存储数据,调用了put方法后,队列里面也是空的。

  • 每一个put操作必须等待一个take操作完成,否则不能添加元素。

  • 适合传递性场景。

  • 性能高于ArrayBlockingQueue和LinkedBlockingQueue。

18.2 SynchronousQueue示例

我们创建了两个线程,一个线程用于生产,一个线程用于消费

  • 生产的线程依次put A、B、C三个值

生产的线程依次put A、B、C三个值
  • 消费线程使用take来消费阻塞队列中的内容,每次消费前,等待5秒

消费线程每隔5s调用take方法
  • 运行结果

t1     put A 
t2     take A 5秒后...t1     put B 
t2     take B 5秒后...t1     put C 
t2     take C 

小结:说明生产线程执行put第一个元素"A" 操作后,需要等待消费者线程take完“A”后,才能继续往下执行代码。

18.1 SynchronousQueue应用场景

  • 吞吐量通常要高于LinkedBlockingQueue。创建线程池时,参数runnableTaskQueue(任务队列),用于保存等待执行的任务的阻塞队列可以选择SynchronousQueue。静态工厂方法Executors.newCachedThreadPool()使用了这个队列

18.2 SynchronousQueue和LinkedTransferQueue的区别

  • SynchronousQueue 不存储元素,而LinkedTransferQueue存储元素。

  • SynchronousQueue 队列里面没有元素,而LinkedTransferQueue可以有多个存储在队列等待传输。

  • LinkedTransferQueue还支持若传输不了,则丢到队列里面去。

  • LinkedTransferQueue还支持若超过一定时间传输不了,则丢到队列里面去。

十九、优先级阻塞PriorityBlockingQueue类

19.1 理解PriorityBlockQueue类

PriorityBlockQueue的原理图
  • PriorityBlockQueue = PriorityQueue + BlockingQueue

  • 之前我们也讲到了PriorityQueue的原理,支持对元素排序。

  • 元素默认自然排序。

  • 可以自定义CompareTo()方法来指定元素排序规则。

  • 可以通过构造函数构造参数Comparator来对元素进行排序。

19.2 PriorityBlockQueue实现了哪些接口

  • LinkedBlockingQueue继承了 BlockingQueue接口,可作为阻塞队列使用

  • LinkedBlockingQueue继承了 AbstractQueue抽象类,具有队列的功能。

  • LinkedBlockingQueue实现了 java.io.Serializable 接口,即可支持序列化,能通过序列化去传输。

二十、延时阻塞DelayQueue类

20.1 理解DelayQueue

DelayQueue原理图
  • DelayQueue = Delayed + BlockingQueue。队列中的元素必须实现Delayed接口。

public class DelayQueue<E extends Delayed> extends AbstractQueue<E>implements BlockingQueue<E> {
  • 在创建元素时,可以指定多久可以从队列中获取到当前元素。只有在延时期满才能从队列中获取到当前元素。

20.2 源码解析

  • 添加元素时,指定延时多久可以从队列中获取元素

public boolean offer(E e, long timeout, TimeUnit unit) {return offer(e);
}
  • 获取元素的方法poll需要等待延时时间过了才能获取到元素

if (first == null || first.getDelay(NANOSECONDS) > 0)return null;
elsereturn q.poll();
poll方法

20.3 应用场景

  • 缓存系统的设计:可以用DelayQueue保存缓存元素的有效期。然后用一个线程循环的查询DelayQueue队列,一旦能从DelayQueue中获取元素时,表示缓存有效期到了。

  • 定时任务调度:使用DelayQueue队列保存当天将会执行的任务和执行时间,一旦从DelayQueue中获取到任务就开始执行。比如Java中的TimerQueue就是使用DelayQueue实现的。

20.4 DelayQueue实现了哪些接口

  • DelayQueue实现了 BlockingQueue接口,可作为阻塞队列使用

这一篇花了很多心思在上面,看官方英文文档、画原理图、写demo代码,排版。这恐怕是市面上最全最细讲解Queue的。


往期推荐

3W字!带你玩转「消息队列」

2020-11-26

这8种常见的SQL错误用法,你还在用吗?

2020-11-27

45 张图深度解析 Netty 架构与原理

2020-12-02

求求你,不要再使用!=null判空了!

2020-12-01


关注我,每天陪你进步一点点!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/545154.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ssh框架常见错误与解决方法

1.Class not Found Exception 异常.---->解决方法&#xff1a;在lib中加入两个jar包&#xff08;spring.jar与struts2- spring-plugin-2.1.8.1.jar&#xff09;&#xff1b;2.使用hql语句时出现java.lang.reflect.InvocationTargetException(即使用hql回调函数带参数时) 或…

使用gzip优化web应用(filter实现)

相关知识&#xff1a; gzip是http协议中使用的一种加密算法,客户端向web服务器端发出了请求后&#xff0c;通常情况下服务器端会将页面文件和其他资源&#xff0c;返回到客户端&#xff0c;客户端加载后渲染呈现&#xff0c;这种情况文件一般都比较大&#xff0c;如果开启Gzip …

肯德尔相关性分析_肯德尔的Tau机器学习相关性

肯德尔相关性分析Before we begin I hope you guys have a basic understanding of Pearson’s and Spearmans correlation. As the name suggests this correlation was named after Maurice Kendall in the year 1938. 在开始之前&#xff0c;我希望你们对皮尔逊和斯皮尔曼的…

40 张图带你搞懂 TCP 和 UDP

我们本篇文章的组织脉络如下运输层位于应用层和网络层之间&#xff0c;是 OSI 分层体系中的第四层&#xff0c;同时也是网络体系结构的重要部分。运输层主要负责网络上的端到端通信。运输层为运行在不同主机上的应用程序之间的通信起着至关重要的作用。下面我们就来一起探讨一下…

android手机两种方式获取IP地址

http://www.cnblogs.com/android100/p/Android-get-ip.html 1.使用WIFI 首先设置用户权限 Xml代码 <uses-permission android:name"android.permission.ACCESS_WIFI_STATE"></uses-permission> <uses-permission android:name"android.permi…

进程、线程、多线程相关总结

进程、线程、多线程相关总结 一、说说概念 1、进程&#xff08;process&#xff09; 狭义定义&#xff1a;进程就是一段程序的执行过程。 广义定义&#xff1a;进程是一个程序关于某个数据集合的一次运行。它是操作系统动态执行的基本单元&#xff0c;在传统的操作系统中&#…

z字扫描和光栅扫描的转换_扫描转换计算机图形中的直线

z字扫描和光栅扫描的转换扫描转换直线 (Scan Converting a Straight Line) For the scan conversion of a straight line, we need the two endpoints. In normal life, if we want to draw a line we simply draw it by using a scale or ruler. But we cant draw a line on t…

TextView 单行显示长文本

android:singleLine"true"//单行显示 android:ellipsize"end"//省略号出现在末尾 http://blog.csdn.net/wxg630815/article/details/8996091

腾讯推出高性能 RPC 开发框架

Tars是基于名字服务使用Tars协议的高性能RPC开发框架&#xff0c;同时配套一体化的服务治理平台&#xff0c;帮助个人或者企业快速的以微服务的方式构建自己稳定可靠的分布式应用。Tars是将腾讯内部使用的微服务架构TAF&#xff08;Total Application Framework&#xff09;多年…

Failed connect to github.com:443; No error

任务目标&#xff1a;将线上已有的https://github.com/eyjian/mooon.git克隆到本地的E:\GitHub\mooon目录问题描述&#xff1a;使用Git的Windows客户端UI工具GitHub执行克隆操作时报错&#xff0c;查看它的日志&#xff0c;难发现问题&#xff0c;于是改用Git的Windows命令行终…

python 程序耗时记录_Python学校的学生身高记录程序

python 程序耗时记录A team of 5 people is assigned with a task to record the heights of students in a school and they have decided to make a python program using class to record all the students height. 由5人组成的小组负责记录学校中学生的身高&#xff0c;他…

看完这篇文章,我再也不怕面试官问「垃圾回收」了...

前言 Java 相比 C/C 最显著的特点便是引入了自动垃圾回收 (下文统一用 GC 指代自动垃圾回收)&#xff0c;它解决了 C/C 最令人头疼的内存管理问题&#xff0c;让程序员专注于程序本身&#xff0c;不用关心内存回收这些恼人的问题&#xff0c;这也是 Java 能大行其道的重要原因之…

react从不会到入门

react从不会到入门1_react初识1.1_react基础环境搭建1.2_文件目录介绍1.2_JSX基础1.2.1_JSX介绍1.2.2_JSX表达式1.2.3_列表渲染1.2.4_条件渲染1.2.5_函数调用1.2.6_样式控制2_组件基础2.1_函数组件2.2_点击事件3_组件通讯3.1_父子关系4_生命周期4.1_挂载阶段4.2_更新阶段5_Hook…

Windows系统端口转发

1、添加端口转发 netsh interface portproxy add v4tov4 listenport10001 listenaddress192.168.1.100 connectport10001 connectaddress192.168.1.105 2、删除端口转发 netsh interface portproxy del v4tov4 listenport10001 listenaddress192.168.1.100 3、查看已存在的端口…

Microsoft Dynamics CRM 数据库连接存储位置在哪里 是在注册表里

Microsoft Dynamics CRM 数据库连接存储位置是在注册表里

将所有文件从目录复制到Python中的另一个目录

shutil (shell utilities) module, provides option to copy the files recursively from src to dst. shutil(shell实用程序)模块 &#xff0c;提供了将文件从src递归复制到dst的选项 。 The syntax to copy all files is: 复制所有文件的语法为&#xff1a; shutil.copytre…

Redis的8大数据类型,写的真好

来源 | blog.itzhouq.cn/redis2最近这几天的面试每一场都问到了&#xff0c;但是感觉回答的并不好&#xff0c;还有很多需要梳理的知识点&#xff0c;这里通过几篇 Redis 笔记整个梳理一遍。Redis 的八大数据类型官网可查看命令&#xff1a;http://www.redis.cn/commands.htmlR…

前后端(react+springboot)服务器部署

前后端&#xff08;reactspringboot&#xff09;服务器部署1_前端reactumi服务器部署1.1_前端生成dist目标文件1.2_准备连接服务器的工具1.3_安装nginx1.4_部署项目1.4.1_传输dist文件1.4.2_配置配置文件1.4.3_启动nginx2_后端springboot项目部署服务器2.1_后端生成目标文件2.2…

关于CentOS-6的默认带的mysql启动和安装问题

一开始想自己一步一步从编译开始搭建一个lanmp环境&#xff1b;从鸟哥的linux看到可以不用自己去安装&#xff0c;默认的可能更稳定&#xff0c;所以就开始探索系统自带的mysql和其他的工具&#xff0c;在mysql启动的时候遇到了问题。问题&#xff1a;默认的系统中根本就没有 【…

【LeetCode从零单排】No 191.Number of 1 Bits(考察位运算)

题目 Write a function that takes an unsigned integer and returns the number of ’1 bits it has (also known as the Hamming weight). For example, the 32-bit integer ’11 has binary representation 00000000000000000000000000001011, so the function should retur…