目录

Java 队列概述

BlockingQueue 阻塞队列

LinkedList 双链表队列

ArrayDeque 双向数组队列

ConcurrentLinkedQueue 并发安全队列

无界延时阻塞队列 DelayQueue


Java 队列概述

1、Queue 队列接口是一种 Collection, 被设计用于处理之前临时保存在某处的元素。除了基本的 Collection 操作之外队列还提供了额外的插入、提取和检查操作。每一种操作都有两种形式:如果操作失败则抛出一个异常;如果操作失败则返回一个特殊值(null或 false,取决于是什么操作)。

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

3、只有优先级队列可以根据提供的比较器对元素进行排序或者是采用正常的排序。无论怎么排序队列的头将通过调用 remove() 或 poll() 方法进行移除。

4、在 FIFO 队列种,所有新的元素被插入到队尾,其他种类的队列可能使用不同的布局来存放元素。

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

常用的java队列 java 队列的使用_数据结构

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 类、LinkedT ransferQueue 类、SynchronousQueue 类、PriorityBlockQueue 类、DelayQueue 类继承了AbstractQueue 抽象类和实现了 BlockingQueue接口
PriorityQueue 类和 ConcurrentLinkedQueue 类继承了 AbstractQueue 抽象类

BlockingQueue 阻塞队列

1、java.util.concurrent.BlockingQueue 阻塞队列是 JDK 1.5 新增的接口,它提供了不同功能的 7 个实现类(JDK7),也就是提供了 7 个不同的阻塞队列。

2、BlockingQueue 提供了线程安全的队列访问方式,是一个支持两个附加操作的队列,在队列为空时,获取元素的线程会等待队列变为非空。当队列满时,存储元素的线程会等待队列可用。

3、java.util.concurrent 并发包下很多高级同步类的实现都是基于 BlockingQueue 。

4、BlockingQueue 不接受 null 元素,试图 add、put 或 offer 一个 null 元素时,某些实现会抛出 NullPointerException,null 被用作指示 poll 操作失败的警戒值。 

5、BlockingQueue 阻塞队列可以是限定容量的,也可以没有容量约束时,如 LinkedBlockingDeque<E>、LinkedBlockingQueue<E> 队列默认大小为 Integer.MAX_VALUE。

6、BlockingQueue 典型应用就是生产者-消费者使用场景,如 socket 客户端数据的读取和解析,读取数据的线程不断将数据放入队列,然后解析线程不断从队列取数据解析。可以安全地与多个生产者和多个使用者一起使用。 

7、BlockingQueue 接口是 Queue 的子接口,它的主要用途并不是作为容器,而是作为线程同步的的工具,因此他具有一个很明显的特性,当生产者线程试图向 BlockingQueue 放入元素时,如果队列已满,则线程被阻塞,当消费者线程试图从中取出一个元素时,如果队列为空,则该线程会被阻塞,正是因为它所具有这个特性,所以在程序中多个线程交替向 BlockingQueue 中放入元素,取出元素,它可以很好的控制线程之间的通信。

四种操作形式

1、对于不能立即满足但可能在将来某一时刻可以满足的操作,BlockingQueue 提供了四组不同的方法,如下表所示:

操作

抛出异常

特殊值

阻塞

超时

插入

add(e)

offer(e)

put(e)

offer(e, time, unit)

移除

remove()

poll()

take()

poll(time, unit)

检查

element()

peek()

不可用

不可用

抛出异常:如果试图的操作无法立即执行,抛出异常
特殊值:如果试图的操作无法立即执行,返回一个特定的值(true / false /null取决于具体操作)。
阻塞:如果试图的操作无法立即执行,该方法调用将会无限期阻塞,直到条件满足后再继续执行
超时:如果试图的操作无法立即执行,该方法调用将会在指定时间内阻塞,直到条件满足后再继续执行或者超时后抛出异常

2、实际项目应该因地制宜的选择不同的方法进行使用

7 个阻塞队列

1、java.util.concurrent.BlockingQueue 阻塞队列提供了不同功能的 7 个实现类(JDK7),也就是提供了 7 个不同的阻塞队列。

ArrayBlockingQueue    

 一个由数组结构组成的有界阻塞队列。此队列按 FIFO(先进先出)原则对元素进行排序,新元素插入到队列的尾部,队列获取操作则是从队列头部开始获得元素。 这是一个典型的“有界缓存区”,一旦创建了这样的缓存区,就不能再增加其容量。试图向已满队列中放入元素会导致操作受阻塞;试图从空队列中提取元素将导致类似阻塞。

LinkedBlockingQueue    

一个由链表结构组成的有界阻塞队列。此队列按 FIFO(先进先出)排序元素。新元素插入到队列的尾部,并且队列获取操作会获得位于队列头部的元素。链接队列的吞吐量通常要高于基于数组的队列,但是在大多数并发应用程序中,其可预知的性能要低。 

可选的容量范围构造方法参数作为防止队列过度扩展的一种方法。如果未指定容量,则它等于 Integer.MAX_VALUE。除非插入节点会使队列超出容量,否则每次插入后会动态地创建链接节点。 

PriorityBlockingQueue    

一个支持优先级排序的无界阻塞队列。此类不允许使用 null 元素。依赖自然顺序的优先级队列也不允许插入不可比较的对象。

DelayQueue    

一个使用优先级队列实现的无界阻塞队列。只有在延迟期满时才能从中提取元素。该队列的头部是延迟期满后保存时间最长的 Delayed 元素。如果延迟都还没有期满,则队列没有头部,并且 poll 将返回 null。此队列不允许使用 null 元素。 

SynchronousQueue    

一个不存储元素的阻塞队列。其中每个插入操作必须等待另一个线程的对应移除操作 ,反之亦然。同步队列没有任何内部容量,甚至连一个队列的容量都没有。

它非常适合于传递性设计,在这种设计中,在一个线程中运行的对象要将某些信息、事件或任务传递给在另一个线程中运行的对象,它就必须与该对象同步。

LinkedTransferQueue    

一个由链表结构组成的无界阻塞队列。这个队列相对于任何给定的生产者订购元素FIFO(先进先出)。 队列的头部是那些已经排队的元素是一些生产者的最长时间。 队列的尾部是那些已经在队列上的元素是一些生产者的最短时间。 

LinkedBlockingDeque    

一个由链表结构组成的双向阻塞队列。可选的容量范围构造方法参数是一种防止过度膨胀的方式。如果未指定容量,那么容量将等于 Integer.MAX_VALUE。只要插入元素不会使双端队列超出容量,每次插入后都将动态地创建链接节点。 

2、实际开发中应该以实际需求合理的选择适合的阻塞队列。

在线代码演示:https://gitee.com/wangmx1993/java-se/blob/master/src/main/java/org/example/queue/blockingQueue

LinkedList 双链表队列

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

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

常用的java队列 java 队列的使用_阻塞队列_02

 3、LinkedList 的增加和删除效率相对较高,而查找和修改的效率相对 ArrayLis 较低。

4、以下情况建议使用 ArrayList:频繁访问列表中的一个元素;只在列表的首尾添加元素。

5、以下情况建议使用 LinkedList:频繁地在列表开头、中间、末尾添加和删除元素;需要通过循环迭代来访问列表中的元素。

6、LinkedList不是线程安全的,所以可以使用如下方式保证线程安全:List list = Collections.synchronizedList(new LinkedList<>());

在线演示源码:https://gitee.com/wangmx1993/java-se/blob/master/src/main/java/org/example/queue/LinkedListTest.java

ArrayDeque 双向数组队列

1、由数组组成的双端队列,实现了 Deque  接口 ‑ 可用于双端队列 。

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

3、不是线程安全的,禁止插入null元素。

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

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

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

在线源码:https://gitee.com/wangmx1993/java-se/blob/master/src/main/java/org/example/queue/ArrayDequeTest.java

ConcurrentLinkedQueue 并发安全队列

1、ConcurrentLinkedQueue 使用的是 CAS 原语无锁队列实现,是一个异步队列,入队的速度很快,出队进行了加锁。

2、ConcurrentLinked 是由链表结构组成的线程安全的先进先出无界队列。

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

4、不允许插入 null 元素,支持非阻塞地访问并发安全的队列,不会抛出 ConcurrentModifiationException 异常。

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

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

常用的java队列 java 队列的使用_java_03

 在线演示源码:https://gitee.com/wangmx1993/java-se/blob/master/src/main/java/org/example/queue/ConcurrentLinkedQueueTest.java

无界延时阻塞队列 DelayQueue

1、DelayQueue = Delayed + BlockingQueue,队列中的元素必须实现 Delayed 接口,实现 getDelay 方法。

2、如下所示源码 take 方法获取元素时需要等待延时时间过了才能获取到元素,即只有当 getDelay 方法值小于等于0时才能拿到元素。

3、应用场景:

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

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

4、注意事项:

(1)服务器重启后,数据全部消失,怕宕机。
(2)集群扩展相当麻烦
(3)因为内存条件限制的原因,比如下单未付款的订单数太多,那么很容易就出现OOM异常

常用的java队列 java 队列的使用_rabbitmq_04

常用的java队列 java 队列的使用_常用的java队列_05

 在线演示源码:

https://gitee.com/wangmx1993/java-se/src/main/java/org/example/queue/delayQueue