Java数据结构10-死磕Java队列-基础篇

本文深入探讨Java中的队列数据结构,包括非阻塞队列与阻塞队列的区别,有界队列与无界队列的特点,以及顺序队列、循环队列和链队列的不同实现方式。此外,还列举了Java中常用的队列类及其特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


title: 死磕Java队列
date: 2020-05-18 15:53:00
categories: 多线程,Java,Queue,队列
description: Java

1. 基本概念

队列是一种特殊的线性表,特殊之处在于它只允许在表的前端进行删除操作,而在表的后端进行插入操作,和栈一样,队列是一种操作受限制的线性表。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。

  • 分类:队列除了保证队列中的元素有序之外,它在基本构造上还在差异,主要体现在 容量 、 阻塞 、 线程安全三个方面。

    • 队列容量:从队列的大小以及容量来说,分为 有界队列无界队列

    • 阻塞:从现场阻塞的特征来说,分为 阻塞队列非阻塞队列,这决定了队列的吞吐性能。

    • 线程安全:从线程安全来说,分为 线程安全非线程安全

  • 特点:队列这种最先进去的数据最先被取来,即 先进先出 的结构,统称为 First In First Out,简称 FIFO。与栈类似,队列中可以操作数据的位置也有一定的限制。在栈中,数据的添加和删除都在同一端进行,而在队列中则分别是在两端进行的。队列也不能直接访问位于中间的数据,必须通过出队操作将目标数据变成首位后才能访问。类似于现实中排队时的队列(队尾进,队头出),插入元素的一端称为队尾,删除(取出)元素的一端称为队头。分别对应于入队和出队操作。

队列除了上述差异,有的地方还把顺序队列、链式队列以及循环队列这三种队列单独拿出来说。

20230808102326

1.1. 非阻塞队列与阻塞队列

1.1.1. 阻塞队列

支持两个附加操作的队列。这两个附加的操作支持阻塞的 插入移除 方法。具体表现在:

  • 队列的容量已满:队列会阻塞插入元素的线程,直到队列不满
  • 队列中元素为空:获取元素的线程会等待队列变为非空

生产数据时,若队列已满,那么生产线程会暂停下来,直到队列中有可以存放数据的地方,才会继续工作;而当我们的消费者向队列中获取数据时,若队列为空,则消费者线程会暂停下来,直到容器中有元素出现,才能进行获取操作。

1.1.2. 非阻塞队列
  • 队列的容量已满:插入元素抛出异常
  • 队列中元素为空:获取元素的为空

1.2. 有界队列与无界队列

  • 有界队列:队列对索要存储元素的上限设置有固定值。队列的容量,队列的容量决定了该队列保存元素的数量上限,这在某些特定场景下,使用 有界队列 可以规避无休止的元素添加而造成内存资源上的开销。

  • 无界队列:队列对索要存储元素的没有设置固定大小,一般理论上的上线就是 Integer.MAX_VALUE 的值,但是从使用者的体验上,就相当于 “无界”。

2. 使用场景

JAVA 中的队列是在 JDK1.5 中被引入,它是一种数据结构,遵循原则就是 FIFO,即 先进先出。它的存在就是是为了解决 削峰填谷 ,给队列下游减轻压力,将生产速度较快的地方进行减速,以便于适应下游的处理速度。

3. 队列实现

线性表有 顺序存储链式存储 ,队列作为一种特殊的线性表,也同样存在这两种存储方式。

3.1. 顺序队列

用数组存储队列,即利用一组地址连续的存储单元依次存放队列中的元素。为了避免当只有一个元素时,队头和队尾重合使得处理变得麻烦,所以引入两个指针(头尾指针):front指针指向队头元素,rear指针指向队尾元素的下一个位置。初始化时的头尾指针,初始值均为0。入队时尾指针rear加1,出队时头指针front加1,头尾指针相等时队列为空。

  • 初始化为空队列,头尾指针相等

20230808103200

  • 每一次有元素入队,尾指针加1,头指针不变,尾指针始终指向队尾元素的下一位

20230808103227

  • 每一次有元素出队,则尾指针不变,头指针加1

20230808103256

  • 最后所有元素出队,则头指针再次和尾指针相等,说明队列空了

20230808103326

  • 当尾指针已经指向了队列的最后一个位置的下一位置时,若再有元素入队,就会发生“溢出”。

20230808103401

3.2. 循环队列

前一章节说到顺序队列的 “假溢出” 问题:队列的存储空间未满,却发生了溢出。比如尾指针现在虽然已经指向了最后一个位置的下一位置,但是之前队头也删除了一些元素,那么头指针经历若干次的加1之后,留出了很多空位置,但是顺序队列还在傻乎乎的以为再有元素入队就溢出呢!肯定不合理。故循环队列诞生!

所以解决"假溢出"的办法就是后面满了,就再从头开始,也就是头尾相接的循环。我们把队列的这种头尾相接的顺序存储结构称为循环队列。将新元素插入到第一个位置上,入队和出队仍按先进先出的原则进行,操作效率高,空间利用率高。

20230808103622

3.3. 链队列

链队列的就是一个操作受限的单向链表,只不过它只能尾进头出而已,我们把它简称为链队列。链式队列和单向链表比就多了两个指针,头指针和尾指针。

相比普通的队列,元素出队时无需移动大量元素,只需移动头指针。而且可动态分配空间,不需要预先分配大量存储空间。适合处理用户排队等待的情况。

20230808103925

20230808103943

3.4. 顺序队列和链式队列的比较

  • 顺序队列是用数组实现的,首指针在出队的时候移动,尾指针在入队的时候移动,需要考虑队列为空和队列为满的两种情况
  • 链式队列是用链表实现的,首指针不移动始终指向头节点,尾指针在入队的时候移动,只考虑队列为空的情况(不用考虑满是因为链表长度在程序运行过程中可以不断增加,只要存储空间够malloc就能申请内存空间来存放节点)

4. Java常用队列

日常使用中一些常用的 JAVA 队列的列表,我们按照是否阻塞、是否线程安全、是否有界作为划分,具体如下:

类名是否阻塞是否线程安全是否有界特点
LinkedList非阻塞线程安全可选有界基于链表
ArrayBlockingQueue阻塞线程安全有界基于数组的有界队列
LinkedBlockingQueue阻塞线程安全可选有界基于链表的无界队列
ConcurrentLinkedQueue非阻塞线程安全无界基于链表 线程安全的队列
PriorityBlockingQueue阻塞线程安全无界基于优先次序的无界队列
PriorityQueue非阻塞非线程安全无界
SynchronousQueue阻塞线程安全无数据队列内部没有容器的队列 较特别
LinkedBlockingQueue阻塞线程安全无界
DelayQueue阻塞线程安全无界基于时间优先级的队列
LinkedTrmsferQueue阻塞线程安全无界基于链表
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

王老邪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值