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

问:JAVA阻塞队列实现类及最佳实践?

在多线程编程中,阻塞队列作为一种关键的数据结构,为线程间安全、高效的数据交换提供了重要支持。Java的java.util.concurrent包中提供了多种阻塞队列的实现,每种实现都有其独特的特点和适用场景。

一、阻塞队列实现类

以下是Java中BlockingQueue接口的主要实现类及差异。

实现类队列结构容量特性公平性支持元素特性特殊功能
ArrayBlockingQueue数组有界,创建时指定且不可变支持无特殊要求
LinkedBlockingQueue链表可选有界,动态调整容量不支持无特殊要求
PriorityBlockingQueue无界无界不支持必须实现Comparable或提供Comparator优先级排序
DelayQueue无界无界不支持必须实现Delayed接口延时获取元素
SynchronousQueue不存储元素无容量概念支持无特殊要求插入与获取操作必须配对
LinkedTransferQueue链表无界不支持无特殊要求直接传输元素给消费者
ConcurrentLinkedQueue(非BlockingQueue链表无界不适用无特殊要求非阻塞,高效并发操作
1. 队列结构
  • ArrayBlockingQueue:采用数组结构,是唯一的数组结构实现。数组结构在访问速度上通常较快,但在插入和删除元素时,可能需要移动大量数据。
  • LinkedBlockingQueuePriorityBlockingQueueDelayQueueLinkedTransferQueue:均采用链表结构。链表结构在插入和删除元素时,通常只需要修改指针,因此效率较高。
  • ConcurrentLinkedQueue:虽然也是链表结构,但它不是BlockingQueue接口的实现,而是提供了非阻塞的并发操作。
2. 容量特性
  • ArrayBlockingQueue:容量在创建时指定且不可变,因此在使用过程中需要合理设置容量大小。
  • LinkedBlockingQueue:容量可以动态调整,通过指定Integer.MAX_VALUE可以创建无界队列。这使得它在处理不确定数量的任务时更加灵活。
  • PriorityBlockingQueueDelayQueue:均为无界队列,没有容量限制。这可能会导致在内存紧张的情况下出现内存溢出的问题。
  • SynchronousQueue:不存储元素,因此没有容量概念。它要求插入与获取操作必须配对,否则线程会阻塞。
  • ConcurrentLinkedQueue:也是无界的,但它是非阻塞的,因此不会出现阻塞等待的情况。
3. 公平性支持
  • ArrayBlockingQueueSynchronousQueue:支持公平的访问队列。这意味着线程可以按照它们加入队列的顺序来访问队列中的元素,从而避免了“饥饿”现象。
  • 其他实现类均不支持公平的访问队列,这可能会导致某些线程长时间得不到执行的机会。
4. 元素特性
  • PriorityBlockingQueue:元素必须实现Comparable接口或提供Comparator比较器,以便在队列中进行优先级排序。这使得它适用于需要按照优先级处理任务的场景。
  • DelayQueue:元素必须实现Delayed接口,该接口定义了元素何时可以被获取。这使得它适用于需要延时获取元素的场景,如定时任务。
  • 其他实现类对元素没有特殊要求,可以存储任意类型的对象。
5. 特殊功能
  • PriorityBlockingQueue:支持优先级排序,可以按照元素的优先级顺序获取元素。
  • DelayQueue:支持延时获取元素,可以在指定的时间后获取元素。
  • SynchronousQueue:要求插入与获取操作必须配对,否则线程会阻塞。这使得它适用于需要严格同步的场景。
  • LinkedTransferQueue:支持直接传输元素给消费者,而无需先将元素存储在队列中。这使得它在某些场景下可以提高效率。
  • ConcurrentLinkedQueue:提供高效的非阻塞并发操作,适用于高并发场景下的队列操作。但它不支持阻塞和超时机制。
二、阻塞队列的最佳实践

阻塞队列在多线程编程中扮演着重要角色,特别是在生产者-消费者模式中。

1. 选择合适的阻塞队列实现

在选择阻塞队列实现时,应根据具体的应用场景和需求来选择合适的实现类。例如,如果需要按照优先级处理任务,可以选择PriorityBlockingQueue;如果需要延时获取元素,可以选择DelayQueue;如果需要严格的同步和配对操作,可以选择SynchronousQueue;如果需要高效的非阻塞并发操作,可以选择ConcurrentLinkedQueue(但需要注意它不支持阻塞和超时机制)。

示例

// 创建一个固定大小的阻塞队列
BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(10);
2. 使用正确的插入和移除方法

阻塞队列提供了put()take()方法,它们会分别阻塞当前线程直到可以插入元素或移除元素。对于非阻塞操作,可以使用offer()poll()方法。在使用这些方法时,需要注意它们的返回值和异常处理。

示例

// 生产者线程向队列中插入元素
public void producer() throws InterruptedException {queue.put(1); // 如果队列满,则阻塞直到可以插入
}// 消费者线程从队列中移除元素
public void consumer() throws InterruptedException {Integer item = queue.take(); // 如果队列空,则阻塞直到可以移除// 处理元素
}
3. 处理中断

在使用阻塞队列时,应该考虑到线程可能被中断的情况。put()take()方法都会抛出InterruptedException,需要适当处理这个异常,通常的做法是恢复中断状态。

示例

// 处理中断的消费者线程
public void consumerWithInterrupt() {try {while (true) {Integer item = queue.take();// 处理元素}} catch (InterruptedException e) {Thread.currentThread().interrupt(); // 恢复中断状态// 处理中断后的逻辑}
}
4. 避免忙等待

在使用非阻塞方法(如offer()poll())时,应该避免忙等待的情况。可以通过适当的等待策略(如Thread.sleep()Object.wait()等)来避免忙等待带来的资源浪费。

示例

// 避免忙等待的消费者线程
public void consumerWithoutBusyWaiting() {while (true) {Integer item = queue.poll();if (item != null) {// 处理元素} else {try {Thread.sleep(100); // 等待一段时间后再尝试} catch (InterruptedException e) {Thread.currentThread().interrupt(); // 恢复中断状态}}}
}
5. 合理设置队列容量

阻塞队列的容量应该根据具体的应用场景来合理设置。过小的容量可能导致频繁的阻塞和唤醒,影响性能;过大的容量则可能占用过多的内存资源,甚至导致内存溢出。因此,在设置队列容量时,需要综合考虑任务的处理速度、内存使用情况以及系统的稳定性等因素。

代码示例

// 创建一个具有合理容量的阻塞队列(这里以100为例)
BlockingQueue<Integer> queueWithCapacity = new LinkedBlockingQueue<>(100);
6. 使用多个队列实现优先级处理

在某些应用中,可能需要处理不同优先级的任务。这时可以使用多个阻塞队列来实现优先级处理。例如,可以创建两个不同优先级的阻塞队列,一个用于存储高优先级任务,另一个用于存储低优先级任务。消费者线程可以先尝试从高优先级队列中获取任务,如果高优先级队列为空,则再从低优先级队列中获取任务。

示例

// 创建两个不同优先级的阻塞队列
BlockingQueue<Integer> highPriorityQueue = new PriorityBlockingQueue<>();
BlockingQueue<Integer> lowPriorityQueue = new LinkedBlockingQueue<>();// 生产者线程向不同优先级的队列中插入元素
public void producerWithPriority() throws InterruptedException {highPriorityQueue.put(1); // 高优先级任务lowPriorityQueue.put(2); // 低优先级任务
}// 消费者线程优先处理高优先级队列中的任务
public void consumerWithPriority() throws InterruptedException {while (true) {Integer highPriorityItem = highPriorityQueue.poll();if (highPriorityItem != null) {// 处理高优先级任务} else {Integer lowPriorityItem = lowPriorityQueue.take(); // 如果高优先级队列为空,则处理低优先级任务// 处理低优先级任务// ...}}
}
7. 使用超时机制

阻塞队列还提供了带有超时参数的方法,如offer(E e, long timeout, TimeUnit unit)poll(long timeout, TimeUnit unit)。这些方法允许线程在指定的时间内尝试插入或移除元素,如果超时则返回失败,而不会一直阻塞。这对于需要限时完成的任务处理非常有用。

示例:

// 尝试在指定时间内向队列中插入元素
public boolean tryOfferWithTimeout(BlockingQueue<Integer> queue, Integer item, long timeout, TimeUnit unit) throws InterruptedException {return queue.offer(item, timeout, unit);
}// 尝试在指定时间内从队列中移除元素
public Integer tryPollWithTimeout(BlockingQueue<Integer> queue, long timeout, TimeUnit unit) throws InterruptedException {return queue.poll(timeout, unit);
}
8. 监控和调优

在实际应用中,应该监控阻塞队列的使用情况,包括队列的长度、等待线程的数量、元素的插入和移除速度等。这些信息可以帮助你了解系统的运行状态,并及时进行调优。例如,如果发现队列长度经常达到上限,可能需要增加队列容量或优化任务处理速度。

9. 考虑异常处理策略

在使用阻塞队列时,还需要考虑异常处理策略。除了处理InterruptedException外,还应该考虑其他可能的异常,如NullPointerException(当尝试插入null元素时)和IllegalStateException(当使用不支持的操作时)。根据具体的应用场景,可以选择合适的异常处理策略,如记录日志、重试操作或终止线程等。

10. 结合其他并发工具使用

阻塞队列通常与其他并发工具结合使用,如线程池(ThreadPoolExecutor)、信号量(Semaphore)和倒计时锁存器(CountDownLatch)等。这些工具可以帮助你更好地管理线程和协调任务执行。例如,你可以使用线程池来提交任务到阻塞队列中,并使用信号量来控制同时执行的任务数量。

三、结语

阻塞队列是多线程编程中非常重要的数据结构,它提供了线程安全的队列操作。在选择和使用阻塞队列时,需要根据具体的应用场景和需求来选择合适的实现类和配置参数。同时,还需要注意处理中断、避免忙等待、合理设置队列容量、使用多个队列实现优先级处理以及使用超时机制等最佳实践。通过结合其他并发工具使用,并考虑异常处理策略和监控调优,可以构建高效稳定的多线程应用。

相关文章:

问:JAVA阻塞队列实现类及最佳实践?

在多线程编程中&#xff0c;阻塞队列作为一种关键的数据结构&#xff0c;为线程间安全、高效的数据交换提供了重要支持。Java的java.util.concurrent包中提供了多种阻塞队列的实现&#xff0c;每种实现都有其独特的特点和适用场景。 一、阻塞队列实现类 以下是Java中Blocking…...

Springboot3 + MyBatis-Plus + MySql + Vue + ProTable + TS 实现后台管理商品分类(最新教程附源码)

Springboot3 MyBatis-Plus MySql Uniapp 商品加入购物车功能实现&#xff08;针对上一篇sku&#xff09; 1、效果展示2、数据库设计3、后端源码3.1 application.yml 方便 AliOssUtil.java 读取3.2 model 层3.2.1 BaseEntity3.2.1 GoodsType3.2.3 GoodsTypeSonVo3.3 Controll…...

消费电子制造企业如何使用SAP系统提升运营效率与竞争力

在当今这个日新月异的消费电子市场中&#xff0c;企业面临着快速变化的需求、激烈的竞争以及不断攀升的成本压力。为了在这场竞赛中脱颖而出&#xff0c;消费电子制造企业纷纷寻求数字化转型的突破点&#xff0c;其中&#xff0c;SAP系统作为业界领先的企业资源规划(ERP)解决方…...

算法记录——树

二叉树 3.1二叉树的最大深度 思路&#xff1a;二叉树的最大深度 根节点的最大高度。因此本题可以转换为求二叉树的最大高度。 而求高度的时候应该采用后序遍历。遍历顺序为&#xff1a;左右中。每次遍历的节点按后序遍历顺序&#xff0c;先收集左右孩子的最大高度&#xff0c;…...

单片机在控制和自动化任务中的应用场景广泛

单片机在控制和自动化任务中的应用场景广泛&#xff0c;以下是一些具体示例&#xff1a; 1. 家电控制 洗衣机&#xff1a;单片机用于控制洗衣周期、温度和水位。微波炉&#xff1a;控制加热时间、功率和用户界面。 2. 工业自动化 生产线监控&#xff1a;单片机用于控制传送…...

UEFI EDK2框架学习(三)——protocol

一、Protocol协议 搜索支持特定Protocol的设备&#xff0c;获取其Handle gBS->LocateHandleBuffer 将内存中的Driver绑定到给定的ControllerHandle gBS->OpenProtocol 二、代码实现 Protocol.c #include <Uefi.h> #include <Library/UefiLib.h> #includ…...

PostgreSQL的字段存储类型了解

PostgreSQL的字段存储类型了解 在 PostgreSQL 中&#xff0c;每个字段&#xff08;列&#xff09;都有其存储类型&#xff0c;这些存储类型决定了数据库如何存储和处理该字段的数据。了解和适当地利用这些存储类型&#xff0c;可以提高数据库的性能和存储效率。 主要的存储类…...

CTFshow 命令执行 web29~web36(正则匹配绕过)

目录 web29 方法一&#xff1a;include伪协议包含文件读取 方法二&#xff1a;写入文件 方法三&#xff1a;通识符 web30 方法一&#xff1a;filter伪协议文件包含读取 方法二&#xff1a;命令执行函数绕过 方法三&#xff1a;写入文件 web31 方法一&#xff1a;filter伪…...

【顺序表使用练习】发牌游戏

【顺序表使用练习】发牌游戏 1. 介绍游戏2. 实现52张牌3. 实现洗牌4. 实现发牌5. 效果展示 1. 介绍游戏 首先先为大家介绍一下设计要求 实现52张牌&#xff08;这里排除大小王&#xff09;洗牌——打乱牌的顺序发牌——3个人&#xff0c;1人5张牌 2. 实现52张牌 创建Code对象创…...

1.7 编码与调制

欢迎大家订阅【计算机网络】学习专栏&#xff0c;开启你的计算机网络学习之旅&#xff01; 文章目录 前言前言1 基本术语2 常用的编码方法2.1 不归零编码2.2 归零编码2.3 反向归零编码2.4 曼彻斯特编码2.5 差分曼彻斯特编码 3 常用的调制方法3.1 调幅&#xff08;AM&#xff09…...

004集—— txt格式坐标写入cad(CAD—C#二次开发入门)

如图所示原始坐标格式&#xff0c;xy按空格分开&#xff0c;将坐标按顺序在cad中画成多段线&#xff1a; 坐标xy分开并按行重新输入txt&#xff0c;效果如下&#xff1a; 代码如下 &#xff1a; using Autodesk.AutoCAD.DatabaseServices; using Autodesk.AutoCAD.Runtime; us…...

CSS中的font-variation-settings:探索字体的可变性

随着Web字体的发展&#xff0c;设计师们不再局限于传统的字体样式。现代Web字体支持可变字体&#xff08;Variable Fonts&#xff09;&#xff0c;这种字体允许开发者在单一的字体文件中包含多种字形样式。通过使用CSS中的font-variation-settings属性&#xff0c;我们可以控制…...

组合优化与凸优化 学习笔记5 对偶拉格朗日函数

有的时候约束条件有点难搞&#xff0c;我们可以把它放到目标函数里面。 记得之前凸函数的时候的结论吗&#xff1f;一大堆函数&#xff0c;每一段都取最大的&#xff0c;最后会得到一个凸函数。同理&#xff0c;每一段都取最小的&#xff0c;得到的是一个凹函数。就这样&#x…...

监控易监测对象及指标之:Exchange邮件服务器监测

在现代企业运营中&#xff0c;邮件服务器的作用至关重要&#xff0c;它不仅承载着企业内外的信息传递&#xff0c;还是协同工作的重要工具。为了确保邮件服务器的稳定运行&#xff0c;以及邮件的顺畅收发&#xff0c;采用高效的监控系统是不可或缺的。监控易作为一款专业的监控…...

【机器学习基础】Transformer学习

Transformer学习 梯度消失FeedForward层激活函数的主要作用是在网络中加入非线性变换 梯度消失 梯度爆炸 FeedForward层 Transformer结构: Transformer结构主要分为两大部分: 一是Encoder层结构:Encoder 的输入由 Input Embedding 和 Positional Embedding 求和输入Multi…...

mysql如何不使用窗口函数,去统计出入库情况

mysql如何不使用窗口函数&#xff0c;去统计出入库情况 你把这个表看做 进出库表&#xff0c;每个物料把时间正序后 依次累加数量 &#xff0c;看这个物料的时间线上 是否会出现负数&#xff0c;1号进货5个 2号出库3个 3号你不能出库3个 最多俩个 不然就是负库存&#xff0c;…...

uni-app canvas文本自动换行

封装 支持单行文本超出换行。多行文本顺位排版 // 填充自动换行的文本function fillFeedText({ctx, text, x, y, maxWidth, lineHeight, color, size}) {// 文本配置ctx.setFontSize(size);ctx.setFillStyle(color);// 计算文本换行宽高&#xff0c;换行逻辑const words text…...

【设计模式-职责链】

定义 职责链模式是一种行为设计模式&#xff0c;**它通过将请求发送给链上的多个处理者来避免请求发送者与处理者之间的紧密耦合。每个处理者可以选择处理请求或将其传递给链中的下一个处理者。**这样&#xff0c;可以将处理请求的责任链式组织&#xff0c;从而实现更灵活的请…...

Prompt:在AI时代,提问比答案更有价值

你好&#xff0c;我是三桥君 随着AI技术的飞速发展&#xff0c;我们进入了一个信息爆炸的时代。在这个时代&#xff0c;只要你会提问&#xff0c;AI就能为你提供满意的答案。这种现象让很多人开始思考&#xff1a;在这个答案触手可及的时代&#xff0c;答案的价值是否还像以前…...

whatis命令:关于命令的简短描述

一、命令简介 ​whatis​ 命令用于查询命令、函数、文件等的基本用途&#xff0c;查询结果只是一句简短的描述。 例如 $ whatis ls ls (1) - list directory contents返回关于 ls 命令的简短描述。这个结果实质是来自于man手册的一个章节&#xff0c;在较新的L…...

shell脚本--常见案例

1、自动备份文件或目录 2、批量重命名文件 3、查找并删除指定名称的文件&#xff1a; 4、批量删除文件 5、查找并替换文件内容 6、批量创建文件 7、创建文件夹并移动文件 8、在文件夹中查找文件...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...

【磁盘】每天掌握一个Linux命令 - iostat

目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat&#xff08;I/O Statistics&#xff09;是Linux系统下用于监视系统输入输出设备和CPU使…...

【项目实战】通过多模态+LangGraph实现PPT生成助手

PPT自动生成系统 基于LangGraph的PPT自动生成系统&#xff0c;可以将Markdown文档自动转换为PPT演示文稿。 功能特点 Markdown解析&#xff1a;自动解析Markdown文档结构PPT模板分析&#xff1a;分析PPT模板的布局和风格智能布局决策&#xff1a;匹配内容与合适的PPT布局自动…...

ios苹果系统,js 滑动屏幕、锚定无效

现象&#xff1a;window.addEventListener监听touch无效&#xff0c;划不动屏幕&#xff0c;但是代码逻辑都有执行到。 scrollIntoView也无效。 原因&#xff1a;这是因为 iOS 的触摸事件处理机制和 touch-action: none 的设置有关。ios有太多得交互动作&#xff0c;从而会影响…...

【Java学习笔记】BigInteger 和 BigDecimal 类

BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点&#xff1a;传参类型必须是类对象 一、BigInteger 1. 作用&#xff1a;适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...

在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案

这个问题我看其他博主也写了&#xff0c;要么要会员、要么写的乱七八糟。这里我整理一下&#xff0c;把问题说清楚并且给出代码&#xff0c;拿去用就行&#xff0c;照着葫芦画瓢。 问题 在继承QWebEngineView后&#xff0c;重写mousePressEvent或event函数无法捕获鼠标按下事…...

Java + Spring Boot + Mybatis 实现批量插入

在 Java 中使用 Spring Boot 和 MyBatis 实现批量插入可以通过以下步骤完成。这里提供两种常用方法&#xff1a;使用 MyBatis 的 <foreach> 标签和批处理模式&#xff08;ExecutorType.BATCH&#xff09;。 方法一&#xff1a;使用 XML 的 <foreach> 标签&#xff…...

Git常用命令完全指南:从入门到精通

Git常用命令完全指南&#xff1a;从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...

android13 app的触摸问题定位分析流程

一、知识点 一般来说,触摸问题都是app层面出问题,我们可以在ViewRootImpl.java添加log的方式定位;如果是touchableRegion的计算问题,就会相对比较麻烦了,需要通过adb shell dumpsys input > input.log指令,且通过打印堆栈的方式,逐步定位问题,并找到修改方案。 问题…...