当前位置: 首页 > article >正文

JUC并发工具---阻塞队列

阻塞队列也就是BlockingQueue,它是一个接口,继承了Queue接口,是队列的一种。

使用阻塞队列的话,就不会发生线程安全问题,降低我们的开发的难度和工作量(不用我们自己考虑线程安全,而是转移到队列上),实现了隔离,提升了安全性。

主要并发队列关系图

[外链图片转存中…(img-7Uko6uLa-1734860138402)]

阻塞队列非阻塞队列Deque
ArrayBlockingQueueConcurrentLinkedQueue 用CAS保证了线程安全双端队列
LinkedBlockingQueue
SynchronousQueue
DelayQueue
PriorityBlockingQueue
LinkedTransferQueue

阻塞队列常用方法

add,remove,element

抛出异常

add():往队列里添加一个元素,如果队列满了,就会抛出异常来提示队列已满

remove():删除元素,如果我们删除的队列是空的,remove方法就会抛出异常

element():返回队列的头部节点但不删除,如果我们用这个方法去操作一个空队列,想获取队列的头结点,可是由于队列是空的,会抛出和前面remove方法一样的异常

offer,poll,peek

返回操作结果但不抛出异常

对应add(),remove(), element()方法

put,take

阻塞

put方法插入元素时:如果队列没有满,就和普通的插入一样是正常的插入,如果队列已满就无法继续插入,则阻塞,直到队列里有了空闲空间。如果后续队列有了空闲空间,队列就会解除阻塞状态,并把需要添加的数据添加到队列中

take 方法的功能:获取并移除队列的头结点,通常在队列里有数据的时候是可以正常移除的,可是一旦执行take方法的时候,队列里无数据,则阻塞,一旦队列里有数据就会立刻解除阻塞状态,并且取到数据

常见的阻塞队列

ArrayBlockingQueueLinkedBlockingQueeuSynchronousQueuePriorityBlockingQueueDelayQueue
有界,创建容量后不可扩容无界,默认容量是整数的最大值Integer.MAX_VALUE容量为0,所做的就是直接传递支持优先级的无界阻塞队列,后续可以扩容无界
内部使用数组存储元素内部使用链表实现可以通过初始化构造器参数Comparator来指定排序规则。插入的对象必须是可以Comparable的可以设定让队列中的任务延迟多久之后执行。
里面的元素必须实现Delayed接口
利用ReentrantLock实现线程安全次取数据都要先阻塞,直到有数据被放入,同理,每次放数据的时候也会阻塞,直到有消费者来取

阻塞和非阻塞队列的并发安全原理

[外链图片转存中…(img-Bu1eKWAi-1734860138402)]

[外链图片转存中…(img-kGcXpc58-1734860138402)]

如何选择

功能比如是否需要阻塞队列帮我们排序,如优先级排序、延迟执行等
如果有这个需要,我们就必须选择类似于PriorityBlockingQueue之类的有排序能力的阻塞队列
容量或者说是否有存储的要求,还是只需要“直接传递”
在考虑这一点的时候,我们知道前面介绍的那几种阻塞队列,有的是容量固定的
如ArrayBlockingQueue;有的默认是容量无限的,如LinkedBlockingQueue;而有的里面没有任何容
量,如 SynchronousQueue;而对于DelayQueue 而言,它的容量固定就是Integer.MAX_VALUE
所以不同阻塞队列的容量是千差万别的,我们需要根据任务数量来推算出合适的容量,从而去选取合适
的 BlockingQueue
能否扩容因为有时我们并不能在初始的时候很好的准确估计队列的大小,因为业务可能有高峰期、低谷期如果一开
始就固定一个容量,可能无法应对所有的情况,也是不合适的,有可能需要动态扩容
如果我们需要动态扩容的话,那么就不能选择ArrayBlockingQueue,因为它的容量在创建时就确定了,
无法扩容
相反,PriorityBlockingQueue即使在指定了初始容量之后,后续如果有需要,也可以自动扩容所以我们
可以根据是否需要扩容来选取合适的队列
内存结构在上一课时我们分析过ArrayBlockingQueue的源码,看到了它的内部结构是“数组”的形式
和它不同的是,LinkedBlockingQueue的内部是用链表实现的
所以这里就需要我们考虑到,ArrayBlockingQueue没有链表所需要的“节点”,空间利用率更高
所以如果我们对性能有要求可以从内存的结构角度去考虑这个问题
性能比如LinkedBlockingQueue由于拥有两把锁,它的操作粒度更细,在并发程度高的时候,相对于只有
一把锁的ArrayBlockingQueue性能会更好
另外,SynchronousQueue性能往往优于其他实现,因为它只需要“直接传递”,而不需要存储的过
程。如果我们的场景需要直接传递的话,可以优先考虑SynchronousQueue

http://www.kler.cn/a/447683.html

相关文章:

  • MFC/C++学习系列之简单记录13
  • LabVIEW伸缩臂参数监控系统
  • golang自定义MarshalJSON、UnmarshalJSON 原理和技巧
  • java中带缓存的输入/输出流
  • VMware安装Ubuntu 16.04以及安装好后初步使用配置!
  • 解锁BL后的K40降级
  • 加电:DETR论文阅读
  • 基于鲲鹏服务器的打砖块小游戏部署
  • 《开启微服务之旅:Spring Boot 从入门到实践》(二)
  • 三种国产大语言模型Python免费调用
  • “在Python中,所有的数据都是对象”是什么意思?
  • 贪心算法求解跳跃游戏
  • 企业内训|高智能数据构建、Agent研发及AI测评技术内训-吉林省某汽车厂商
  • 对计算机网络中“层”的理解
  • Elasticsearch:什么是提示工程 - prompt engineering?
  • aosp15 - Activity生命周期切换
  • Ubuntu安装vue/cil(vue脚手架)
  • 排序算法 (插入,选择,冒泡,希尔,快速,归并,堆排序)
  • 【MAC】深入浅出 Homebrew 下 Nginx 的安装与配置指南
  • 探索 Vue.js 组件开发:从基础到进阶的完整指南
  • 基于单片机的火灾报警器 (论文+源码)
  • 选择排序和冒泡排序;MySQL架构
  • 数字化制造新生态:共话无代码+AI落地实践
  • 调整docker bridge地址冲突,通过bip调整 bridge地址
  • 【Redis】缓存
  • unity 最小后监听键盘输入