javaEE初阶——多线程(五)

在这里插入图片描述

T04BF

👋专栏: 算法|JAVA|MySQL|C语言

🫵 小比特 大梦想

此篇文章与大家分享关于多线程的文章第五篇关于 多线程代码案例二 阻塞队列
如果有不足的或者错误的请您指出!

目录

    • 2.阻塞队列
      • 2.1常见队列
      • 2.2 生产者消费者模型
        • 有利于进行解耦合
        • 程序进行削峰填谷
      • 2.3通过代码看一下阻塞队列和生产者消费者模型
      • 2.4自己实现阻塞队列
        • 实现一个普通的循环队列
        • 保证线程安全
        • 加入阻塞

2.阻塞队列

2.1常见队列

关于队列,我们之前学过的队列是最普通的队列,但是实际上队列还有其他几个版本,总体可以分成一下几类:
(1)普通队列
(2)优先级队列
对于上述两种队列,都是线程不安全的
(3)阻塞队列
这种队列就是线程安全的,且实现了阻塞功能

所谓阻塞功能就是,当队满了的时候,此时如果要往队列里面插入元素,就会触发阻塞等待,直到队列不满为止

同理,如果队列为空,此时如果要往队列里面拿元素,也会触发阻塞等待,直到队列不为空为止

BlockingQueue就是标准库提供的阻塞队列

(4)消息队列
我们知道队列的性质就是先进先出
但是对于消息队列来说就不是普通的先进先出,而是通过topic这样的参数,来对不同的数据进行分类
在出队列的时候,是按照指定topic里面的参数来 进行 先进先出 的

举个例子就是
在医院做检查的时候,有时候一个检查室可能不只是检查一种器官
在这里插入图片描述
如果按照如图所示的排队顺序,如果医生指定检查的是哪种器官,那么对应的器官检查就相当于 一个 topic,那么此时的先后顺序是按照这个topic来的

而消息队列往往也是带有阻塞功能的

2.2 生产者消费者模型

我们在上面讲到的 阻塞队列 和 消息队列 都能实现生产者消费者模型
而实现所谓的生产者消费者模型,在实际开发中又有两方面的意义
(1)方便进行解耦合
(2)有利于程序进行削峰填谷
下面我们就来讲讲所谓的 “生产者消费者模型”

有利于进行解耦合

如果是在耦合比较高的情况下
在这里插入图片描述
此时A客户端要是想给B服务器发送请求,意味着A中的代码就要包含很多关于 B服务器的逻辑,
此时就有了一定的耦合
那么如果A中的代码逻辑一旦进行修改,那么在B中也需要对应的修改
同时如果A/ B 出现问题,另一方也很有可能被影响到

但是如果我们采用生产者消费者模型,引入的消息队列
在这里插入图片描述
此时站在A的视角是不知道B的存在的,只关心与消息队列的交互
同时,站在B的视角是不知道A的存在的,只关心与 消息队列的交互
那么此时A 和 B之间的耦合就很小了
更重要的是,如果此时引入 C服务器,那么也只是需要让C直接从消息队列里面拿数据即可

程序进行削峰填谷

在这里插入图片描述
此时A对于接受到的请求只是进行一些简单的操作,而B相对进行的是重量级操作
而某一时刻 A 收到的数据激增,此时B进行的操作也会激增,消耗的资源多,B就容易挂

当我们引入生产者消费者模型
在这里插入图片描述
那么此时无论A给队列写多块,B都可以按照自己固有的节奏来消费数据,B的节奏就不一定完全跟着A了,相当于把B保护起来了
但是此时效率是一定会有折损的,不太适合对于响应速度比较高的场景

2.3通过代码看一下阻塞队列和生产者消费者模型

在这里插入图片描述
BIockingQueue是一个接口
java提供了3个类供我们使用
在这里插入图片描述
阻塞队列只需要考虑,入队列和出队列即可,阻塞队列没有"取队首元素"操作(也不是完全没有,只不过这些操作没有阻塞功能)
阻塞队列也提供了offer和poll方法,这两个是不带有阻塞功能的,我们实际上用的是put和take方法

利用阻塞队列实现生产者消费者模型

    public static void main(String[] args) {BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(100);Thread t1 = new Thread(() -> {//生产int count = 0;while (true) {try {System.out.println("生产了" + count);queue.put(count++);} catch (InterruptedException e) {throw new RuntimeException(e);}}});Thread t2 = new Thread(() -> {//消费while (true) {try {System.out.println("消费了" + queue.take());Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t1.start();t2.start();}

此时就能实现,当队列满了的时候,要往队列里面插入元素就需要 阻塞等待
在这里插入图片描述

2.4自己实现阻塞队列

实现一个普通的循环队列
public class MyBlockingQueue {private String[] elems;//[head,tail) head -> 位置指的是第一个元素    tail -> 指的是最后一个元素的下一个元素 以实现循环队列private int head = 0;private int tail = 0;private int size = 0;public MyBlockingQueue(int capacity) {this.elems = new String[capacity];}public String take() {if(size == 0) {//后面实现阻塞return null;}String ret =  elems[head];head++;if (head == elems.length) {head = 0;}this.size--;return ret;}public void put (String elem) {if(size >= elem.length()) {//后面实现阻塞return;}this.elems[tail++] = elem;if(tail == elems.length) {tail = 0;}this.size++;}
}
保证线程安全
public class MyBlockingQueue {private String[] elems;//[head,tail) head -> 位置指的是第一个元素    tail -> 指的是最后一个元素的下一个元素 以实现循环队列private int head = 0;private int tail = 0;private int size = 0;public MyBlockingQueue(int capacity) {this.elems = new String[capacity];}public String take() {synchronized (this) {if(size == 0) {//后面实现阻塞return null;}String ret =  elems[head];head++;if (head == elems.length) {head = 0;}this.size--;return ret;}}public void put (String elem) {synchronized (this) {if(szie >= elems.length) {//后面实现阻塞return;}this.elems[tail++] = elem;if(tail == elems.length) {tail = 0;}this.size++;}}
}
加入阻塞
public class MyBlockingQueue {private String[] elems = null;//[head,tail) head -> 位置指的是第一个元素    tail -> 指的是最后一个元素的下一个元素 以实现循环队列private int head = 0;private int tail = 0;private int size = 0;public MyBlockingQueue(int capacity) {this.elems = new String[capacity];}public String take() throws InterruptedException {synchronized (this) {if(size == 0) {this.wait();//阻塞,直到有元素put的时候唤醒}String ret =  elems[head];head++;if (head == elems.length) {head = 0;}this.size--;this.notify();return ret;}}public void put (String elem) throws InterruptedException {synchronized (this) {if(size >= elems.length) {this.wait();//阻塞,直到有元素take的时候唤醒}this.elems[tail++] = elem;if(tail == elems.length) {tail = 0;}this.size++;this.notify();}}
}

那么我们此时就可以利用我们自己的阻塞队列来实现生产者消费者模型
在这里插入图片描述
在这里插入图片描述

但是实际上还会存在一个问题
在java官方文档中
在这里插入图片描述
其实就是说,wait不只是能够被notify唤醒,比如interrupt也行,但是如果我们的程序用try-catch处理异常,程序就会继续往下允许,那么简单使用if判断的时候,如果不是我们设定的notify唤醒wait,程序往下运行就会出bug

因此我们最好搭配while使用,是最稳妥的做法,即被唤醒的时候,再次确认一下,看看条件是否成立

因此最后的版本就是:

public class MyBlockingQueue {private String[] elems = null;//[head,tail) head -> 位置指的是第一个元素    tail -> 指的是最后一个元素的下一个元素 以实现循环队列private int head = 0;private int tail = 0;private int size = 0;public MyBlockingQueue(int capacity) {this.elems = new String[capacity];}public String take() throws InterruptedException {synchronized (this) {while(size == 0) {this.wait();//阻塞,直到有元素put的时候唤醒}String ret =  elems[head];head++;if (head == elems.length) {head = 0;}this.size--;this.notify();return ret;}}public void put (String elem) throws InterruptedException {synchronized (this) {while(size >= elems.length) {this.wait();//阻塞,直到有元素take的时候唤醒}this.elems[tail++] = elem;if(tail == elems.length) {tail = 0;}this.size++;this.notify();}}
}
感谢您的访问!!期待您的关注!!!

在这里插入图片描述

T04BF

🫵 小比特 大梦想

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

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

相关文章

走近网络安全公司:F5探索应用安全至简之道

伴随企业加速数字化转型工作、扩展到新的基础设施环境并采用微服务架构&#xff0c;企业正在拥抱混合和多云基础设施所带来的灵活性。现在跨越四种环境部署应用的企业&#xff0c;其平均需要管理和保护的应用数据路径比应用仅限本地部署时多10倍&#xff0c;复杂性呈指数级增加…

2024数学建模时间汇总与竞赛攻略

目录 2024数学建模汇总&#xff08;时间、报名费、获奖率、竞赛级别、是否可跨校&#xff09; 中国高校大数据挑战赛 “华数杯”国际大学生数学建模竞赛 美国大学生数学建模竞赛&#xff08;美赛&#xff09; 数学中国&#xff08;认证杯&#xff09;数学建模网络挑战赛 …

盲盒一番赏小程序:打开未知的惊喜之旅

在快节奏的生活中&#xff0c;人们总是渴望寻找一份属于自己的小确幸。盲盒一番赏小程序&#xff0c;正是这样一个为你带来无尽惊喜与乐趣的平台。我们精心打造这一小程序&#xff0c;让每一次点击都成为一次全新的探索&#xff0c;让每一次选择都充满无限可能。 盲盒一番赏小…

JavaWeb--05Vue项目简介

Vue项目简介 1 创建vue项目2 Vue项目目录结构3 运行Vue项目3 Vue项目开发流程 1 创建vue项目 环境准备好了&#xff0c;接下来我们需要通过Vue-cli创建一个vue项目&#xff0c;然后再学习一下vue项目的目录结构。Vue-cli提供了如下2种方式创建vue项目: 命令行&#xff1a;直接…

用Cmake编译程序时,链接到FFmpeg库

用Cmake编译程序时&#xff0c;链接到FFmpeg库 一、前言 可喜可贺&#xff0c;折腾了一晚上终于把这个勾八链接成功了&#xff0c;已经要吐了。看到下面控制台的输出&#xff0c;吾心甚慰呀&#x1f62d; [100%] Linking CXX executable rknn_yolov5_demo [100%] Built targe…

SpringBoot 操作 Redis

导入对应版本的依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId> </dependency>修改配置文件中的信息 spring:redis:host: 127.0.0.1port: 8888注意: 我这里 xsh…

【python】深度解剖!一文搞懂字符串常用功能

【python】深度解剖!一文搞懂字符串常用功能 【先赞后看养成习惯】求点赞+关注+收藏😀 目录 【python】深度解剖!一文搞懂字符串常用功能字符串的创建:字符串的格式化——占位符.format方法字符串的分割字符串的连接字符串的重复字符串的大小写转换字符串的判断字符串的…

sherpa + ncnn 离线语音识别

目录结构 前言音视频格式转为wavsherpa-ncnn编译LinuxWindowswindows编译中遇到的问题问题“nmake -? failed with: no such file or directory”编译失败原因 成功编译截图 可执行程序说明模型下载语言识别测试LinuxWindows 参考文献 前言 小编需要实现离线音视频语言部分识…

C++ 程序的内存分配

C 程序的内存分配 C 程序的内存分配栈堆数据区程序代码区参考 C 程序的内存分配 一个 C 编译的程序占用内存分为以下几个部分&#xff08;从高地址到低地址&#xff09;&#xff1a; 内核空间&#xff1a;由操作系统创建并控制&#xff0c;用户代码不能读写。栈&#xff1a;由…

mysql 日环比 统计

接到一个任务&#xff0c;要计算日环比的情况。 16、查询销售额日环比情况 日环比&#xff1a; &#xff08;今日-昨日&#xff09;/ 昨日 的一个比率情况。 1&#xff0c;建表 DROP TABLE IF EXISTS sale; create table sale(id int not null AUTO_INCREMENT,record_date da…

IDEA插件:CodeGeex

前言 CodeGeeX是由清华大学和智谱AI联合开发的多语言代码生成模型。CodeGeeX是一款AI编程助手&#xff0c;其功能类似于Github Copilot、Codeium、CodeWhisperer、Bito等智能编程助手。CodeGeeX支持Python、C、Java、JavaScript、Go等10多种主流编程语言。它可以帮助程…

windows驱动开发-WDM框架(一)

在前面的文章中解释过&#xff0c;NT5.0之后windows确定了新的架构Windows Driver Model (WDM)&#xff0c;在Vista之后又推出了Windows Driver Framework(WDF)&#xff0c;这两个都属于驱动程序框架&#xff0c;那么它们的之间的关系是怎样的&#xff1f; WDF是对WDM进行的封…

利用大语言模型,矢量数据库实现数据库的智能搜索

目的 数据库使用SQL 语言查询数据&#xff0c;数据库的记录中要有一个关键字段&#xff08;通常称为主键字段&#xff0c;它的值在数据库列表中是唯一的&#xff09;,数据记录是结构化的. 如果你需要根据数据记录的内容来查询数据记录&#xff0c;就需要通过Select 语句在数据库…

OpenCV杂记(1):绘制OSD(cv::getTextSize, cv::putText)

1. 简述 我们使用OpenCV时&#xff0c;有时会在图像的某个位置绘制OSD信息&#xff0c;如绘制一些字符串作为指示信息。 本文将简要介绍在图像&#xff08;cv::Mat&#xff09;上绘制固定的字符串信息。 2. 使用的API &#xff08;1&#xff09;cv::getTextSize() CV_EXPORT…

vue3 删除对象中的属性,可以使用js里的delete,但需注意ts定义对象类型!

如上如&#xff0c;当使用delete 删除stateData中的属性时&#xff0c; 报错&#xff0c;意思为 TypeScript 错误“‘delete’ 运算符的操作数必须是可选的 什么原因呢&#xff1f;是因为我偷懒 缺少了ts定义类型 方法一&#xff1a; &#xff08;不推荐&#xff09; delete …

MultiHeadAttention在Tensorflow中的实现原理

前言 通过这篇文章&#xff0c;你可以学习到Tensorflow实现MultiHeadAttention的底层原理。 一、MultiHeadAttention的本质内涵 1.Self_Atention机制 MultiHeadAttention是Self_Atention的多头堆嵌&#xff0c;有必要对Self_Atention机制进行一次深入浅出的理解&#xff0c;这…

Linux Makefile用法

1、什么是makefile&#xff1f; Makefile&#xff1a;将不同模块放在不同的目录中&#xff0c;定义一系列的规则进行 “自动化编译”2、Makefile写法 vim makefile 填写样例&#xff1a; app:sub.c add.c mult.c div.c main.cgcc sub.c add.c mult.c div.c main.c -o app3、工作…

刷代码随想录有感(39):每层最大值

题干&#xff1a; 代码&#xff1a; /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *left;* TreeNode *right;* TreeNode() : val(0), left(nullptr), right(nullptr) {}* TreeNode(int x) : val(x), left(nullptr), …

OpenCV基本图像处理操作(十一)——图像特征Sift算法

图像尺度空间 在一定的范围内&#xff0c;无论物体是大还是小&#xff0c;人眼都可以分辨出来&#xff0c;然而计算机要有相同的能力却很难&#xff0c;所以要让机器能够对物体在不同尺度下有一个统一的认知&#xff0c;就需要考虑图像在不同的尺度下都存在的特点。 尺度空间的…

《6G数据面架构研究》

目录 一、数据服务的定义二、6G数据服务驱动力及面临的挑战6G数据服务的业务驱动6G数据服务的技术驱动6G数据服务的网络内在驱动6G数据面面临的挑战 三、6G数据服务典型场景自动化网络运维用户体验提升通信感知数据服务 四、6G数据面架构研究数据面架构视图功能定义说明&#x…