AQS应用之BlockingQueue详解

概要

AQS全称是 AbstractQueuedSynchronizer,中文译为抽象队列式同步器。BlockingQueue,是java.util.concurrent 包提供的用于解决并发生产者 - 消费者问题的最有用的类,它的特性是在任意时刻只有一个线程可以进行take或者put操作,并且BlockingQueue提供了超时return null的机制,在许多生产场景里都可以看到这个工具的身影。

队列类型

  1. 无限队列 (unbounded queue ) - 几乎可以无限增长
  2. 有限队列 ( bounded queue ) - 定义了最大容量

队列数据结构

队列实质就是一种存储数据的结构

  • 通常用链表或者数组实现
  • 一般而言队列具备FIFO先进先出的特性,当然也有双端队列(Deque)优先级队列
  • 主要操作:入队(EnQueue)与出队(Dequeue)

常见的4种阻塞队列

  • ArrayBlockingQueue 由数组支持的有界队列
  • LinkedBlockingQueue 由链接节点支持的可选有界队列
  • PriorityBlockingQueue 由优先级堆支持的无界优先级队列
  • DelayQueue 由优先级堆支持的、基于时间的调度队列

ArrayBlockingQueue

队列基于数组实现,容量大小在创建ArrayBlockingQueue对象时已定义好

数据结构如下图:

队列创建:

BlockingQueue<String> blockingQueue = new ArrayBlockingQueue<>();

应用场景

在线程池中有比较多的应用,生产者消费者场景

工作原理

基于ReentrantLock保证线程安全,根据Condition实现队列满时的阻塞

LinkedBlockingQueue

是一个基于链表的无界队列(理论上有界)

BlockingQueue<String> blockingQueue = new LinkedBlockingQueue<>();

上面这段代码中,blockingQueue 的容量将设置为 Integer.MAX_VALUE 。

向无限队列添加元素的所有操作都将永远不会阻塞,[注意这里不是说不会加锁保证线程安全],因此它可以增长到非常大的容量。

使用无限 BlockingQueue 设计生产者 - 消费者模型时最重要的是 消费者应该能够像生产者向队列添加消息一样快地消费消息 。否则,内存可能会填满,然后就会得到一个 OutOfMemory 异常。

DelayQueue

由优先级堆支持的、基于时间的调度队列,内部基于无界队列PriorityQueue实现,而无界队列基于数组的扩容实现。

队列创建:

BlockingQueue<String> blockingQueue = new DelayQueue();

要求

入队的对象必须要实现Delayed接口,而Delayed集成自Comparable接口

应用场景

电影票

工作原理:

队列内部会根据时间优先级进行排序。延迟类线程池周期执行。

BlockingQueue API

BlockingQueue 接口的所有方法可以分为两大类:负责向队列添加元素的方法和检索这些元素的方法。在队列满/空的情况下,来自这两个组的每个方法的行为都不同。

添加元素

方法

说明

add()

如果插入成功则返回 true,否则抛出 IllegalStateException 异常

put()

将指定的元素插入队列,如果队列满了,那么会阻塞直到有空间插入

offer()

如果插入成功则返回 true,否则返回 false

offer(E e, long timeout, TimeUnit unit)

尝试将元素插入队列,如果队列已满,那么会阻塞直到有空间插入

检索元素

方法

说明

take()

获取队列的头部元素并将其删除,如果队列为空,则阻塞并等待元素变为可用

poll(long timeout, TimeUnit unit)

检索并删除队列的头部,如有必要,等待指定的等待时间以使元素可用,如果超时,则返回 null

在构建生产者 - 消费者程序时,这些方法是 BlockingQueue 接口中最重要的构建块。

多线程生产者-消费者示例

接下来我们用“迟早药丸”的场景创建一个由两部分组成的程序 - 生产者 ( Producer ) 和消费者 ( Consumer ) 。

生产者将生成一个 0 到 100 的随机数(十全大补丸的编号),并将该数字放在 BlockingQueue 中。我们将创建 16 个线程(潘金莲)用于生成随机数并使用 put() 方法阻塞,直到队列中有可用空间。

需要记住的重要一点是,我们需要阻止我们的消费者线程无限期地等待元素出现在队列中。

从生产者(潘金莲)向消费者(武大郎)发出信号的好方法是,不需要处理消息,而是发送称为毒 ( poison ) 丸 ( pill ) 的特殊消息。 我们需要发送尽可能多的毒 ( poison ) 丸 ( pill ) ,因为我们有消费者(武大郎)。然后当消费者从队列中获取特殊的毒 ( poison ) 丸 ( pill )消息时,它将优雅地完成执行。

以下生产者的代码:

@Slf4j
public class NumbersProducer implements Runnable {private BlockingQueue<Integer> numbersQueue;private final int poisonPill;private final int poisonPillPerProducer;public NumbersProducer(BlockingQueue<Integer> numbersQueue, int poisonPill, int poisonPillPerProducer) {this.numbersQueue = numbersQueue;this.poisonPill = poisonPill;this.poisonPillPerProducer = poisonPillPerProducer;}public void run() {try {generateNumbers();} catch (InterruptedException e) {Thread.currentThread().interrupt();}}private void generateNumbers() throws InterruptedException {for (int i = 0; i < 100; i++) {numbersQueue.put(ThreadLocalRandom.current().nextInt(100));log.info("潘金莲-{}号,给武大郎的泡药!",Thread.currentThread().getId());}for (int j = 0; j < poisonPillPerProducer; j++) {numbersQueue.put(poisonPill);log.info("潘金莲-{}号,往武大郎的药里放入第{}颗毒丸!",Thread.currentThread().getId(),j+1);}}
}

我们的生成器构造函数将 BlockingQueue 作为参数,用于协调生产者和使用者之间的处理。我们看到方法 generateNumbers() 将 100 个元素(生产100副药给武大郎吃)放入队列中。它还需要有毒 ( poison ) 丸 ( pill ) (潘金莲给武大郎下毒)消息,以便知道在执行完成时放入队列的消息类型。该消息需要将 poisonPillPerProducer 次放入队列中。

每个消费者将使用 take() 方法从 BlockingQueue 获取一个元素,因此它将阻塞,直到队列中有一个元素。从队列中取出一个 Integer 后,它会检查该消息是否是毒 ( poison ) 丸 ( pill )(武大郎看潘金莲有没有下毒) ,如果是,则完成一个线程的执行。否则,它将在标准输出上打印出结果以及当前线程的名称。

@Slf4j
public class NumbersConsumer implements Runnable {private BlockingQueue<Integer> queue;private final int poisonPill;public NumbersConsumer(BlockingQueue<Integer> queue, int poisonPill) {this.queue = queue;this.poisonPill = poisonPill;}public void run() {try {while (true) {Integer number = queue.take();if (number.equals(poisonPill)) {return;}log.info("武大郎-{}号,喝药-编号:{}",Thread.currentThread().getId(),number);}} catch (InterruptedException e) {Thread.currentThread().interrupt();}}
}

需要注意的重要事项是队列的使用。与生成器构造函数中的相同,队列作为参数传递。我们可以这样做,是因为 BlockingQueue 可以在线程之间共享而无需任何显式同步。

既然我们有生产者和消费者,我们就可以开始我们的计划。我们需要定义队列的容量,并将其设置为 10个元素。

我们创建4 个生产者线程,并且创建等于可用处理器数量的消费者线程:

public class Main {public static void main(String[] args) {int BOUND = 10;int N_PRODUCERS = 16;int N_CONSUMERS = Runtime.getRuntime().availableProcessors();int poisonPill = Integer.MAX_VALUE;int poisonPillPerProducer = N_CONSUMERS / N_PRODUCERS;int mod = N_CONSUMERS % N_PRODUCERS;BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(BOUND);//潘金莲给武大郎熬药for (int i = 1; i < N_PRODUCERS; i++) {new Thread(new NumbersProducer(queue, poisonPill, poisonPillPerProducer)).start();}//武大郎开始喝药for (int j = 0; j < N_CONSUMERS; j++) {new Thread(new NumbersConsumer(queue, poisonPill)).start();}//潘金莲开始投毒,武大郎喝完毒药GGnew Thread(new NumbersProducer(queue, poisonPill, poisonPillPerProducer + mod)).start();}}

BlockingQueue 是使用具有容量的构造创建的。我们正在创造 4 个生产者和 N 个消费者(武大郎)。我们将我们的毒 ( poison ) 丸 ( pill )消息指定为 Integer.MAX_VALUE,因为我们的生产者在正常工作条件下永远不会发送这样的值。这里要注意的最重要的事情是 BlockingQueue 用于协调它们之间的工作。

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

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

相关文章

MySQL之导入导出远程备份

目录 一. navicat导入导出 二. mysqldump命令导入导出 导入 导出 三. load data infile命令导入导出 导入 导出 四. 远程备份 导入 导出 思维导图 一. navicat导入导出 导入&#xff1a;右键➡运行SQL文件 导出&#xff1a;选中要导出的表➡右键➡转储SQL文件➡数据和结…

C# Emgu.CV4.8.0读取rtsp流录制mp4可分段保存

【官方框架地址】 https://github.com/emgucv/emgucv 【算法介绍】 EMGU CV&#xff08;Emgu Computer Vision&#xff09;是一个开源的、基于.NET框架的计算机视觉库&#xff0c;它提供了对OpenCV&#xff08;开源计算机视觉库&#xff09;的封装。EMGU CV使得在.NET应用程序…

x-cmd pkg | vhs - 将终端的操作过程录制成视频文件的终端录制工具

目录 简介首次用户声明式录制脚本其他功能竞品和相关作品进一步阅读 简介 vhs 是一个命令行录制工具&#xff0c;用于将终端的操作过程录制成视频文件。是由 Charmbracelet 团队使用 Go 开发的&#xff0c;首个版本发布于 2022 年 10 月。开源不到一个月有接近 8k 的 star。 …

Kubernetes (八) 金丝雀发布

一. 金丝雀发布作用&#xff1a; 金丝雀发布是指在生产环境中逐步推出新版本应用程序&#xff0c;只在一小部分用户或流量中使用该版本&#xff0c;并根据反馈逐步扩…

Redis(三)持久化

文章目录 RDB&#xff08;Redis Database&#xff09;自动触发保存频率修改dump文件保存路径修改文件保存名称dump恢复 手动触发save![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/a56fdff44aee4efa96c2ce3615b69dc1.png)bgsave 优劣优点缺点 检查修复dump文件会触…

手把手教你使用cubemx配置串口

重写fput函数 #include <stdio.h>#ifdef __GNUC__#define PUTCHAR_PROTOTYPE int _io_putchar(int ch)#else#define PUTCHAR_PROTOTYPE int fputc(int ch, FILE *f)#endif /* __GNUC__*//*******************************************************************brief R…

安全狗获评CCIA“2023年度先进会员单位”

1月4日&#xff0c;2023年度中国网络安全产业联盟会员大会暨理事会在北京成功举办。 作为国内云原生安全领导厂商&#xff0c;安全狗也受邀出席此次活动。 厦门服云信息科技有限公司&#xff08;品牌名&#xff1a;安全狗&#xff09;创办于2013年&#xff0c;是国内领先的云…

你的企业知识库该更新啦,这样搭建高效又轻松

很明显&#xff0c;企业知识库已经成为我们工作中必不可少的资源&#xff0c;它的价值已经不言而喻&#xff0c;无论是促进团队合作&#xff0c;还是帮助我们更有效地找到收集和使用知识&#xff0c;知识库都发挥着重要的作用&#xff0c;然而&#xff0c;你的企业知识库已经过…

springboot——消息中间件

消息的概念 从广义角度来说&#xff0c;消息其实就是信息&#xff0c;但是和信息又有所不同。信息通常被定义为一组数据&#xff0c;而消息除了具有数据的特征之外&#xff0c;还有消息的来源与接收的概念。通常发送消息的一方称为消息的生产者&#xff0c;接收消息的一方称为…

OV5640 摄像头的图像平滑处理

如图所示&#xff0c;这是整个视频采集系统的原理框图。 上电初始&#xff0c;FPGA 需要通过 IIC 接口对 CMOS Sensor 进行寄存器初始化配置。这些初始化的基本参数&#xff0c;即初始化地址对应的初始化数据都存储在一个预先配置好的 FPGA 片内 ROM中。在初始化配置完成后&…

遥感影像-语义分割数据集:云数据集详细介绍及训练样本处理流程

原始数据集详情 简介&#xff1a;该云数据集包括150张RGB三通道的高分辨率图像&#xff0c;在全球不同区域的分辨率从0.5米到15米不等。这些图像采集自谷歌Earth的五种主要土地覆盖类型&#xff0c;即水、植被、湿地、城市、冰雪和贫瘠土地。 KeyValue卫星类型谷歌Earth覆盖区…

[足式机器人]Part2 Dr. CAN学习笔记-动态系统建模与分析 Ch02-8 Bode Plot伯德图

本文仅供学习使用 本文参考&#xff1a; B站&#xff1a;DR_CAN Dr. CAN学习笔记-动态系统建模与分析 Ch02-8 Bode Plot伯德图 Bode Plot 手绘技巧与应用

第7章-第6节-Java中的Map集合

1、HashMap&#xff1a; 1&#xff09;、 引入 如果业务需要我们去用姓名的拼音手写字母匹配完整姓名&#xff0c;那么如果用单列数据&#xff0c;我们可能需要两个集合才能存储&#xff0c;而且两个集合之间没有关联不好操作&#xff0c;这种时候双列数据就会起很大作用 2&…

【前端素材】bootstrap4实现服装鞋饰电商平台Doron

一、需求分析 一个服装鞋饰电子商务页面是一个在线平台&#xff0c;用于展示和销售各种服装、鞋子和配饰产品。它通常具有以下功能&#xff1a; 产品展示&#xff1a;服装鞋饰电子商务页面会展示各种服装、鞋子和配饰产品的图片、描述和价格。这些产品可以按照不同的分类&#…

批量剪辑方法:掌握视频剪辑技巧,按指定时长轻松分割视频

在视频制作和编辑过程中&#xff0c;经常要批量处理和剪辑大量的视频片段。学会批量剪辑方法可以提高工作效率&#xff0c;还可以使视频编辑更加准确和高效。下面来看下云炫AI智剪如何按指定时长轻松分割视频的批量剪辑方法。 分割后的视频文件效果&#xff0c;已分割分段的视…

PHPStudy快速搭建网站并结合内网穿透远程访问本地站点

文章目录 [toc]使用工具1. 本地搭建web网站1.1 下载phpstudy后解压并安装1.2 打开默认站点&#xff0c;测试1.3 下载静态演示站点1.4 打开站点根目录1.5 复制演示站点到站网根目录1.6 在浏览器中&#xff0c;查看演示效果。 2. 将本地web网站发布到公网2.1 安装cpolar内网穿透2…

减少客户流失并留住长期客户:实用策略与实践分享

一、什么是客户流失&#xff1f; 客户流失是指客户停止付费并离开您的产品/服务。流失率是指在特定时间内离开产品/服务的用户除以用户总数的百分比。 比如&#xff1a; 假设某媒体平台是一个基于订阅的流媒体平台&#xff0c;为用户提供各种电影、电视剧、纪录片等。到二月…

Linux--防火墙,实验案例:基于区域、服务、端口的访问控制

实验环境 某公司的Web服务器&#xff0c;网关服务器均采用Linux CentOS 7.3操作系统&#xff0c;如图2.13所示。为了 加强网络访问的安全性&#xff0c;要求管理员熟悉firewalld防火墙规则的编写&#xff0c;以便制定有效、可行的主机防护策略。 需求描述 > 网关服务器ens3…

RT-Thread:ADC 框架应用,通过 STM32CubeMX 配置 STM32 ADC驱动

关键词&#xff1a;ADC,RT-Thread ADC,STM32 ADC应用 说明&#xff1a;本笔记是记录如何开启 RT-Thread 框架的ADC功能&#xff0c;使用系统自带的ADC函数&#xff0c;并通过 STM32CubeMX 配置 STM32 ADC驱动 。 1. 打开board.h 文件&#xff0c;找到ADC 使用配置的流程&…

猴子选大王

思路&#xff1a;首先举个例子&#xff1a;当N 5 时 1 2 3 4 5 3 3 3 3 输出4 请观看代码 …