【多线程】阻塞队列 | put()方法 | take()方法 | 生产者-消费者模式 |实现阻塞队列

文章目录

    • 阻塞队列
      • 1.生产者-消费者模式
        • 生产者消费者模型的意义:
          • 1.解耦合
          • 2.削峰填谷:
      • 2.阻塞队列的使用
            • BlockingQueue
      • 3.实现阻塞队列
          • 唤醒:
          • 使用阻塞队列实现生产者消费者模型


阻塞队列

阻塞队列是一种特殊的队列:

  • 1.是线程安全的。

  • 2.带有阻塞特性

    如果队列为空,继续出队列,就会发生阻塞。直到其他线程往队列中添加队列为止

    如果队列为满,继续入队列, 也会发生阻塞,直到其他线程从队列中取走元素为止

阻塞队列可以来实现生产者-消费者模型。

1.生产者-消费者模式

生产者:把生产出来的内容,放到阻塞队列中。

消费者:从阻塞队列中获取内容。

生产者消费者模型的意义:
1.解耦合

两个模块联系越紧密,耦合就越高。对于分布式系统来说,更加有意义。

在这里插入图片描述

可以使用生产者-消费者模型,实现解耦合的效果。

在这里插入图片描述

2.削峰填谷:

峰:短时间内,请求量比较多时。

谷:请求量比较少时。

在这里插入图片描述

​ 在这种情况下:高峰时段,一旦客户端发起的请求量非常多时,每个A收到的请求,都会立即发给B。此时,A和B的访问量是相同的。但是在实际上,由于不同的服务器,上面跑的业务不同。虽然访问量一样,单个访问,消耗的硬件资源是不一样的。可能服务器A可以承担这些并发量,但是服务器B承担不了,就会挂掉。

在引入了生产者-消费者模型之后,就会解决这类问题。

在这里插入图片描述

  • 当服务器A收到了大量请求之后,A会把对应的请求写入到队列中。B仍然按照之前的节奏来处理请求。(削峰)
  • 一般情况下,峰值不会持续存在,峰值过后,A的请求量就会恢复正常、甚至减低。服务器B就可以在此时,逐渐把积压的请求给处理掉。(填谷)。

2.阻塞队列的使用

BlockingQueue

在这里插入图片描述

  • BlockingQueue是一个具体的接口,所以需要new一个具体的实现。

  • 同时BlockingQueue继承自Queue。也可以使用Queue的方法(没有阻塞属性)

    1.基于数组实现

    2.基于链表实现

  • BlockingQueue带有阻塞的方法:

    ​ put:阻塞式入队列

    ​ take:阻塞式出队列

    没有提供阻塞式获取队首元素的方法。

    public static void main(String[] args) {// BlockingQueue<String> queue = new ArrayBlockingQueue<>();BlockingQueue<String>queue = new LinkedBlockingQueue<>();queue.put("111");queue.put("222");queue.put("333");queue.put("444");String elem = queue.take();System.out.println(elem);elem = queue.take();System.out.println(elem);elem = queue.take();System.out.println(elem);elem = queue.take();System.out.println(elem);}

3.实现阻塞队列

给一个普通的队列加上线性安全和阻塞

对入队和出队的方法进行加锁。对数据的修改实现原子性操作,保证线程安全

  • put入队的时候,如果队列满了,就进行阻塞(wait)

在出队的时候,当size–后,队列中有位置了,调用notify()方法,对阻塞的put方法进行唤醒。

  • 同样的,如果take出队列时,队列为空的话,也需要进行阻塞。

在入队时,当size++后,队列不为空了,调用notify()方法,对阻塞的take方法进行唤醒。

在这里插入图片描述

  • 一个队列的阻塞情况,要么为空、要么为满。

    put和take只有一边能阻塞。如果put阻塞了,其他线程继续调用put,也会进行阻塞。只有靠take来唤醒。

    take阻塞,其他线程继续调用take也会进行阻塞,只能靠put来唤醒。

唤醒:

wait方法除了使用notify()方法进行唤醒,还可以通过interrupt()方法,来中断wait的状态。

使用interrupt方法唤醒的时候,会出现InterruptedException异常

public void put(String elem) throws InterruptedException {}

因为是throws抛出的异常,执行到interrupt()方法后,整个方法就会结束。

    public void put(String elem) {synchronized (this) {if (size == data.length) {try {this.wait();}catch (InterruptedException e){ }}data[tail] = elem;tail++;if (tail == data.length) {tail = 0;}size++;this.notify();}
  • 如果是try-catch来处理异常。如果出现异常,程序仍会继续执行下去。在满队列的情况下。强行修改,会覆盖掉tail的值,并且size会超出数组长度。

​ 使用wait时,要考虑wait是notify唤醒的,还是通过Interrupt唤醒的。在wait返回时,还要进行判断wait执行的条件符不符合。可以直接将wait写在while循环中。循环的条件就是wait执行的条件。使wait在唤醒之后,再确定一下,条件是否满足。

            while (size == data.length) {//队列满了,就会进行堵塞this.wait();}
  • 最终再通过volatile修饰要频繁修改的变量,避免出现内存可见性问题。
class MyBlockingQueue {private String[] data = new String[1000];private  volatile int head = 0;//队列起始位置private volatile int tail = 0;//队列结束位置的下一个元素。private volatile int size = 0;//队列中有效元素个数//入队public void put(String elem) throws InterruptedException {synchronized (this) {while (size == data.length) {//队列满了,就会进行堵塞this.wait();}//队列没满,向队列添加元素data[tail] = elem;tail++;if (tail == data.length) {//满了之后,环形队列要回到开头。tail = 0;}size++;this.notify();//唤醒take中的wait}}//出队public String take() throws InterruptedException {synchronized (this) {while (size == 0) {//队列为空时this.wait();}//队列不空时,把队首head位置删除String ret = data[head];head++;if (head == data.length) {head = 0;}size--;this.notify();//唤醒put中的wait.return ret;}}
}
使用阻塞队列实现生产者消费者模型
    public static void main(String[] args) {MyBlockingQueue queue = new MyBlockingQueue();//消费者Thread t1 = new Thread(() -> {while (true){try {String res =  queue.take();System.out.println("消费元素: "+res);Thread.sleep(500);} catch (InterruptedException e) {throw new RuntimeException(e);}}});//生产者Thread t2 = new Thread(() -> {int num = 1;while (true){try {queue.put(num+" ");System.out.println("生产元素:"+num);num++;} catch (InterruptedException e) {throw new RuntimeException(e);}}});t1.start();t2.start();}生产元素:1001
生产元素:1002
消费元素: 2 
消费元素: 3 
生产元素:1003
消费元素: 4 
生产元素:1004
  • 生产者快速生产了1000多个,消费者才消耗几个。队列填满之后,生产者进入了阻塞。直到消费者消费了之后,才会进行生产。消费一个生产一个。

点击移步博客主页,欢迎光临~

偷cyk的图

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

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

相关文章

Vue 阶段练习:记事本

将 Vue快速入门 和 Vue 指令的学习成果应用到实际场景中&#xff08;如该练习 记事本&#xff09;&#xff0c;我们能够解决实际问题并提升对 Vue 的技能掌握。 目录 功能展示 需求分析 我的代码 案例代码 知识点总结 功能展示 需求分析 列表渲染删除功能添加功能底部统计…

网络—DAY4

思维导图 多进程并发服务器 #include<myhead.h> #define SER_IP "192.168.122.56" #define SER_PORT 8888 void handler(int signo) {if(signoSIGCHLD){while(waitpid(-1,NULL,WNOHANG)>0);} } int main(int argc, char *argv[]) {//将SIGCHLD信号与处理…

项目中,如何写 readme.md 文件 | 写项目总结

tips&#xff1a;注意写 1. readme文件&#xff1a;①项目文档&#xff08;项目需求和设计文档、项目系统架构和技术文档、接口文档&#xff09;、②项目结构、③启动项目。具体结构见下文。 2. 项目总结&#xff1a;技术栈、描述、主要工作&#xff01;&#xff01;需求及功…

MySQL常见故障现象分析及解决办法

一、背景 MySQL作为广泛使用的关系型数据库管理系统&#xff0c;在日常使用中难免会遇到各种故障。本文将通过一个具体的案例&#xff0c;分析MySQL常见的故障现象&#xff0c;并提供相应的解决办法和代码示例。 二、故障现象 某企业使用的MySQL数据库服务器近期出现以下问题…

FastAPI 是干啥的

FastAPI 是一个现代、快速&#xff08;高性能&#xff09;的 Web 框架&#xff0c;用于构建 API&#xff0c;与 Python 3.6 类型提示一起使用。它基于标准 Python 类型提示&#xff0c;使用 Python 3.6 的类型注解来声明请求参数和返回值的类型&#xff0c;这使得代码既简洁又易…

递归、搜索与回溯算法——递归

T04BF &#x1f44b;专栏: 算法|JAVA|MySQL|C语言 &#x1faf5; 小比特 大梦想 此篇文章与大家分享递归,搜索与回溯算法关于递归的专题 如果有不足的或者错误的请您指出! 目录 1.什么时候使用递归2.汉诺塔2.1解析2.2题解 3.合并两个有序链表3.1解析3.2题解 4.翻转链表4.1解析4…

mysql SQL必知语法

文章目录 概要技术细节小结 概要 mysql SQL必知语法 SQL必知语法包括SELECT、FROM、WHERE、GROUP BY、HAVING、ORDER BY等关键字&#xff0c;用于查询和操作数据库中的数据。SELECT用于选择需要查询的列&#xff0c;FROM用于指定数据表&#xff0c;WHERE用于设置查询条件&…

下载软件时的Ubuntu x86_64-v2、skylake、aarch64版本分别代表什么?

Ubuntu-x86_64-v2、Ubuntu-x86_64-skylake和Ubuntu-aarch64都是Ubuntu的不同版本或变种&#xff0c;它们之间的主要区别在于所支持的硬件架构和针对特定硬件的优化。 Ubuntu-x86_64-v2&#xff1a; 这是基于x86_64&#xff08;也称为AMD64或Intel 64&#xff09;架构的Ubuntu版…

Ubuntu上根据关键字模糊查找指定文件夹或文件

在Ubuntu上根据关键字模糊查找指定文件夹或文件&#xff0c;可以使用以下方法&#xff1a; GNOME 桌面搜索&#xff1a; 使用GNOME桌面的Spotlight式搜索功能&#xff0c;通过按下Super键&#xff08;通常是Windows键&#xff09;并输入关键字&#xff0c;可以搜索文件和文件夹…

人类连接的桥梁:探索Facebook如何连接世界

随着技术的发展和全球化的进程&#xff0c;我们的世界正在变得越来越紧密相连。在这个过程中&#xff0c;社交媒体平台扮演了一个至关重要的角色&#xff0c;为人们提供了一个跨越国界、文化和语言的交流平台。其中&#xff0c;Facebook作为全球最大的社交媒体平台&#xff0c;…

rancher踩坑日志:prometheus访问kubelet 10250端口提示鉴权失败

该原因是因为kubectl禁止了非授权用户访问10250端口来获取node的数据。 解决思路&#xff1a; 添加prometheus访问kubelet时带上证书进行验证匹配 --> 由于我的prometheus是rancher安装的&#xff0c;不知道要怎么修改所以研究了一会没研究明白就放弃了。设置prometheus访问…

Rockchip Android13 Vold(二):Framework层

目录 前言 1、接收VolumeInfo状态 2、通知VolumeInfo状态变化 3、创建StorageVolume

Create2024百度AI开发者大会记录

去年2023.3.16日百度文心大模型发布&#xff0c;今天2024.4.16日 代码智能体&#xff1a;思考模型代码解释器 思考模型整合提示&#xff0c;输入给代码解释器 代码解释器出结果&#xff0c;返回给思考模型&#xff0c;然后迭代 智能代码助手 baidu Comate 多模型推理 种子模型…

JavaScript入门--循环

JavaScript入门--循环 一、for循环二、for in语句三、break语句四、continue语句五、while循环六、do-while语句一、for循环 先来看一个循环案例: for (i = 0; i < 5; i++) {

如何批量删除不包含指定关键字的数据行?

一、需求 有一个报名表&#xff0c;包括年龄&#xff0c;地址&#xff0c;特长等数据&#xff0c;现在想删除特长一列中不含“篮球”这个关键字行&#xff0c;可以使用这些办法。 二、删除数据 方法1.自动筛选&#xff0c; 1.1 选中数据后&#xff0c;点击数据选项卡&#…

java 中公有类的产量如何定义

在Java编程语言中,“公有类”和“产量”的概念并不直接相关联。 “公有类”(public class)是指在Java中,可以被任何其他类访问的类。如果你想要定义一个公有类,你将在类声明处使用 public 关键字: // 这是一个公有类的例子 public class MyClass {// 类体部分,包含字段…

微信人脉扩张!多号批量自动加好友,你get到了吗?

微信是我们在拓展社交圈和寻找商业机会时&#xff0c;与更多的人建立联系的重要渠道。但是&#xff0c;手动一个个添加好友显然费时费力&#xff0c;这时候&#xff0c;微信管理系统的批量自动加好友功能就成为了微信人脉扩张的神器。 通过微信管理系统&#xff0c;我们可以轻…

Java中如何提取视频文件的缩略图

在Java中&#xff0c;可以使用FFmpeg库来提取视频文件的缩略图。以下是一种使用FFmpeg的方法来提取视频缩略图的示例代码&#xff1a; import java.io.File; import java.io.IOException;public class VideoThumbnailExtractor {public static void main(String[] args) {Stri…

IO流-打印流

简介 为打印而生的IO流 打印流的继承体系 优点 高效方便打印的是啥就是啥&#xff0c;不会瞎转&#xff08;不会把97转成a&#xff09; PrintStream try(PrintStream ps new PrintStream("test7.txt", "UTF-8"); ){ps.println(97); // 打印流的优点就是原…

三个晚上!给干废了!MINI2440 挂载 NFS

虚拟机执行&#xff1a;sudo ifconfig tap0 10.10.10.1 up qemu 开发板&#xff1a; set bootargs noinitrd root/dev/nfs rw nfsroot10.10.10.1:/nfsroot ip10.10.10.10:10.10.10.1 ::255.255.255.0 consolettySAC0,115200 Hit any key to stop autoboot: 0 MINI2440 # set…