什么是BlockingQueue?一次性说清了

BlockingQueue(阻塞队列)详解 在新增的Concurrent包中,BlockingQueue很好的解决了多线程中,如何高效安全“传输”数据的问题。通过这些高效并且线程安全的队列类,为我们快速搭建高质量的多线程程序带来极大的便利。本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景。一旦获得了锁之后,如果队列为空,那么将阻塞;否则调用dequeue()出队一个元素。 阅读详情

最近在研究一些并发方面的技术其中研究到阻塞队列(BlockingQueue)的时候做了写笔记文档 大家可以一起探讨一下 :

BlockingQueue,是  java.util.concurrent  包提供的用于解决并发生产者 - 消费者问题的最有用的类。

它的特性是在任意时刻只有一个线程可以进行take或者put操作,并且BlockingQueue提供了超时return null的机制,在许多生产场景里都可以看到这个工具的身影。

一、队列类型

1.无限队列 (unbounded queue ) - 几乎可以无限增长

2.有限队列 ( bounded queue ) - 定义了最大容量

二、队列数据结构

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

  • 通常用链表或者数组实现

  • 一般而言队列具备FIFO先进先出的特性,当然也有双端队列(Deque)优先级队列

  • 主要操作:入队(EnQueue)与出队(Dequeue)

三、常见的4中阻塞队列

  • ArrayBlockingQueue 由数组支持的有界队列

  • LinkedBlockingQueue 由链接节点支持的可选有界队列

  • PriorityBlockingQueue 由优先级堆支持的无界优先级队列

  • DelayQueue 由优先级堆支持的、基于时间的调度队列

1.ArrayBlockingQueue

队列基于数组实现,容量大小在创建ArrayBlockingQueue对象时已定义好数据结构如下图:

       

①队列创建:

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

②应用场景:

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

③工作原理:

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

2.LinkedBlockingQueue

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

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

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

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

使用无限 BlockingQueue 设计生产者 - 消费者模型时最重要的是 消费者应该能够像生产者向队列添加消息一样快地消费消息 。

否则,内存可能会填满,然后就会得到一个 OutOfMemory 异常。

3.DelayQueue 

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

①队列创建:

 BlockingQueue<String> blockingQueue = new DelayQueue();      

②要求:

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

③应用场景:

电影票

④工作原理:

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

4.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 final int poisonPill;
private final int poisonPillPerProducer;
private BlockingQueue<Integer> numbersQueue;
public NumbersProducer(BlockingQueue<Integer> numbersQueue,
int poisonPill,
int poisonPillPerProducer) {
this.numbersQueue = numbersQueue;
this.poisonPill = poisonPill;
this.poisonPillPerProducer = poisonPillPerProducer;
  }
@Override
  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 final int poisonPill;
private BlockingQueue<Integer> queue;
public NumbersConsumer(BlockingQueue<Integer> queue, int poisonPill) {
this.queue = queue;
this.poisonPill = poisonPill;
  }
@Override
  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 ();
    }
//潘金莲开始投毒,武大郎喝完毒药GG
    new Thread (new NumbersProducer (queue, poisonPill, poisonPillPerProducer + mod)).start ();
  }
}

BlockingQueue 是使用具有容量的构造创建的。我们正在创造 4 个生产者和 N 个消费者(武大郎)。

我们将我们的毒 ( poison ) 丸 ( pill )消息指定为 Integer.MAX_VALUE,因为我们的生产者在正常工作条件下永远不会发送这样的值。

这里要注意的最重要的事情是 BlockingQueue 用于协调它们之间的工作。

STM32CUBEIDE调试中HardFault_Handler崩溃的故障分析器实战解析 本文详细解析STM32CUBEIDE调试中HardFault_Handler崩溃问题的实战处理方法。通过内置故障分析器自动诊断硬件异常原因,如内存越界、堆栈溢出等,提供图形化界面简化调试流程,并分享配置步骤、典型案例及高级调试技巧,帮助开发者快速定位和解决嵌入式系统崩溃问题。 阅读详情

相关推荐

告别转换超时!手把手教你用Allegro2Altium.bat搞定AD21导入Allegro 17.2文件

本文详细介绍了如何使用Allegro2Altium.bat工具解决AD21导入Allegro 17.2文件时的转换超时问题。通过理解转换机制、准备离线环境、执行完整转换流程及高级技巧,帮助工程师高效完成PCB设计文件转换,提升工作效率。

weixin_30378623的博客 424

六:抽象队列同步器AQS应用之BlockingQueue详解

目录 概要 队列类型 队列数据结构 ArrayBlockingQueue LinkedBlockingQueue DelayQueue BlockingQueue API 多线程生产者-消费者示例 并发编程脑图:https://www.processon.com/view/link/5d81dec7e4b04c14c4e7aac8 概要 BlockingQueue,是java.util.concurrent包提供的用于解决并发生产者 - 消费者问题的最有用的类,它的特性是在任意时刻只.

Xx__WangQi的博客 861

HACK RF学习之旅记录1——填坑历程

在家隔离期偶尔发现RTL SDR,是realtek芯片破解了做软件无线电,价格非常实在不到百元,功能可以玩电视 广播等等。后来搜搜发现更强大的Hack RF ONE开发板,淘宝和闲鱼都看了大多都是Hack RF ONE单板999,便宜的闲鱼有600多的。还有一块扩展板是MCU+屏+时钟,看起来也很实用(非常推荐)。通过闲鱼某卖家找了渠道,从上海一家店的淘宝下单优惠价格(据说是原厂,超出想象,暗号是...

ZForce2009的博客 1万+

BlockingQueue(阻塞队列)

BlockingQueue 阻塞队列(BlockingQueue) 是一个支持两个附加操作的队列。这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空。当队列满时,存储元素的线程会等待队列可用。阻塞队列常用于生产者和消费者的场景,生产者是往队列里添加元素的线程,消费者是从队列里拿元素的线程。阻塞队列就是生产者存放元素的容器,而消费者也只从容器里拿元素。 使用场景: 多线程并发处理,线程池! 队列 FIFO先进先出 一端写入一端取出 写入如果队列满了就必须阻塞等待 如果队列是空的必须阻塞等待

Student111w的博客 7865

STM32G474多通道ADC电压采集:软件触发与单次转换实战

本文详细介绍了STM32G474多通道ADC电压采集的实战方法,重点讲解了软件触发与单次转换的实现技巧。通过硬件设计、HAL库配置、多通道轮询采集及数据处理等环节的详细解析,帮助开发者高效完成精确电压采集任务。文章还提供了常见问题调试技巧和性能优化建议,适用于需要高精度电压监测的嵌入式应用场景。

weixin_28720767的博客 466

Qt OpenGL 3D 点云数据显示

在Qt中使用OpenGL显示3D点云数据是一个常见的需求,特别是在计算机视觉、机器人学和三维重建等领域。下面我将介绍一个基本的实现方案。

深之JohnChen的专栏 1239

muduo之BlockingQueue

BlockingQueue是muduo是无界队列,利用队列(deque)实现,向队列中加入和取出元素用互斥量和条件变量结合的方式来操作,就是一个线程同步的问题。 BlockingQueue.h // Use of this source code is governed by a BSD-style license // that can be found in the L...

1万+

ROS2 Humble + TurtleBot4仿真环境搭建与避坑实战

本文详细介绍了ROS2 Humble与TurtleBot4仿真环境的搭建与避坑实战,包括环境配置、依赖安装、仿真调试和性能优化等关键步骤。通过保姆级教程和实用技巧,帮助开发者快速搭建稳定的机器人仿真环境,提升开发效率。特别推荐使用Ubuntu 22.04原生系统以避免常见问题。

weixin_30897233的博客 399

Java初中级面试:说一下 BlockingQueue

是立即返回的,适用于非阻塞检查或轮询场景,但通常不是使用阻塞队列的主要目的。方法可以一次性从队列中移除所有可用元素并放入指定集合,提高效率。在 catch 后应恢复中断状态(),并执行清理或退出逻辑。

牛马baby的博客 856

电商数据分析18——电商广告投放的数据分析与优化

在当今的电商行业中,广告投放是提高品牌知名度、吸引潜在客户和增加销售额的关键策略之一。随着市场竞争的加剧,仅仅依靠直观判断和传统方法已经无法满足广告投放的需求。数据分析在这里扮演了至关重要的角色,它可以帮助企业更准确地定位目标受众,评估广告效果,优化广告内容,从而提高广告效率和投资回报率(ROI)。

theskylife的博客 5703

国科大学习资料--数据挖掘(刘莹)--第三章习题及参考答案

国科大学习资料–数据挖掘(刘莹)–第三章习题及参考答案 国科大学习资料–数据挖掘(刘莹)–第三章习题及参考答案 国科大学习资料–数据挖掘(刘莹)–第三章习题及参考答案 第 3 章分类与回归 3.1简述决策树分类的主要步骤。 答:决策树生成的过程如下: (1)对数据源进行数据预处理, 得到训练集和测试集; (2)对训练集进行训练; (3)对初始决策树进行树剪枝; (4)由所得到的决策树提取分类规则; (5)使用测试数据集进行预测,评估决策树模型; 3.2给定决策树,选项有:(1)将决策树转换成规则,然后对结果

无名何许人的博客 3094

BlockingQueue

文章目录BlockingQueue接口的继承关系BlockingQueue(阻塞队列)特点BlockingQueue常用方法ArrayBlockingQueueArrayBlockingQueue简单使用LinkedBlockingQueuePriorityBlockingQueueSynchronousQueue基于BlockingQueue实现生产者消费者 BlockingQueue接口的继承...

念念不忘,必有回响 942

全志F1C100S/F1C200S学习笔记(11)——spi-flash 启动全流程适配烧录及踩坑指南

SPI Flash 系统编译 spi-flash 启动适配 分区规划: 分区序号 分区大小 分区作用 地址空间及分区名 mtd0 1MB (0x100000) spl+uboot 0x0000000-0x0100000 : “uboot” mtd1 64KB (0x10000) dtb文件 0x0100000-0x0110000 : “dtb” mtd2 4MB (0x400000) linux内核 0x0110000-0x0510000 : “kernel” mtd3 剩余 (

丨晋丨 1万+

BlockingQueue深入解析-BlockingQueue看这一篇就够了

BlockingQueue深入解析-BlockingQueue看这一篇就够了 转载:https://www.cnblogs.com/WangHaiMing/p/8798709.html 本篇将详细介绍BlockingQueue,以下是涉及的主要内容: BlockingQueue的核心方法 阻塞队列的成员的概要介绍 详细介绍DelayQueue、ArrayBlockingQueue、Lin...

thedarkclouds的博客 928

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

线程池的核心——BlockingQueue

前言 本篇文章会介绍BlockingQueue的概念。 BlockingQueue是java线程池的核心。无论是在面试还是工作中,都是必知必会的知识点。 BlockingQueue的名字就完美概括了这个接口的特点,Queue 是BlockingQueue的父接口,说明它本质还是一个队列。Blocking意为阻塞,说明BlockingQueue相对于普通队列,新增了阻塞的特性。 本文会从BlockingQueue的父接口Queue开始聊起,包括Queue在java 容器中的定位以及API等等。然后介绍Bloc

asd13518662的博客 1246

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

BlockingQueue详解

注意:该随笔内容完全引自http://wsmajunfeng.iteye.com/blog/1629354,写的很好,非常感谢,复制过来算是个积累,怕以后找不到。 一. 前言   在新增的Concurrent包中,BlockingQueue很好的解决了多线程中,如何高效安全“传输”数据的问题。通过这些高效并且线程安全的队列类,为我们快速搭建高质量的多线程程序带来极大的便利。本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景。 二. 认识BlockingQueu

xiaolegeaizy的博客 570

汉书HE8,好用的汇编代码编写工具

汉书HE8,好用的汇编代码编写工具早期编写汇编代码所用功能强大

BlockingQueue的使用

BlockingQueue是一个特殊的队列,如果BlockQueue是空的,从BlockingQueue取东西的操作将会被阻断进入等待状态,直到BlockingQueue进了东西才会被唤醒.同样,如果BlockingQueue是满的,任何试图往里存东西的操作也会被阻断进入等待状态,直到BlockingQueue里有空间才会被唤醒继续操作.  前言:      在新增的Concurr

航天飞哥的博客 852

blockingqueue java_Java BlockingQueue

Java BlockingQueue在本教程中,我们将学习Java BlockingQueue接口及其方法。Java Collections框架的BlockingQueue接口扩展了Queue接口。它允许任何操作等待,直到成功执行。例如,如果我们要从空队列中删除元素,则阻塞队列允许删除操作等待,直到队列中包含一些要删除的元素。实现BlockingQueue的类由于BlockingQueue是一个接...

weixin_33518565的博客 304

使用BlockingQueue

最近公司里有个同事,发现自己写的接口中返回数据很慢,最后排查后发现,有一个写日志的操作很费时间。 由于接口的返回和写日志操作没有耦合,因此可以在完成接口的操作处理后,新启一个线程去写日志,然后返回结果即可,从而可以降低接口返回数据的延时。 使用BlockingQueue作为中间件,生产者线程(service层)将不需要返回结果值的任务直接放在BlockingQueue中,然后消费者线程不断从B...

cdfvb的博客 357
上一篇: 35岁程序员的毕业季?大厂花式裁员着实厉害……
下一篇: 一场由Char/Varchar 引起的战争
架构文摘JGWZ
博客等级 码龄5年 8808粉丝 345原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值