JavaEE:多线程代码案例(阻塞队列)

news2024/11/15 3:48:29

文章目录

  • 什么是阻塞队列?
  • 使用阻塞队列有什么好处?
  • 代价
  • BlockingQueue的使用
  • 自己实现一个阻塞队列(基于数组)

什么是阻塞队列?

阻塞队列是在普通的(先进先出)的基础上,做出了扩充.

  1. 线程安全
  2. 具有阻塞特性
    a. 如果队列为空,进行出队列操作,此时就会出现阻塞,一直阻塞到其他线程往队列里添加元素为止
    b. 如果队列为满,进行入队列操作,此时也会出现阻塞,一直阻塞到其他线程从队列里取走元素为止.

(标准库中原有的队列Queue和其子类,默认都是线程不安全的)
通常谈到的"阻塞队列"是代码中的一个数据结构,但是由于这个东西太好用了,以至于会把这样的数据结构单独封装成一个服务器程序,并且在单独的服务器机器上进行部署.
此时,这样的阻塞队列,有了一个新的名字—“消息队列”(Message Queue,简称MQ)

基于阻塞队列,最大的应用场景就是实现"生产者消费者模型".
什么是"生产者消费者模型"呢?
举个栗子,大年三十包饺子.
在这里插入图片描述
第一种典型的包法,我们三个人,每个人都分别进行擀饺子皮和包饺子这两操作.但是这种方式其实是比较低效的,我们三个人就需要竞争擀面杖,就可能涉及到阻塞等待~

第二种典型的包法,也是实际生活中大家普遍采取的方案.
我负责擀饺子皮,其他两个人负责包.


使用阻塞队列有什么好处?

使用生产者消费者模型,主要有两方面的好处

  1. 服务器之间的"解耦合"(模块之间的关联程度/影响程度)
    在这里插入图片描述引入生产者消费者模型后,结构就成了下列摸样
    在这里插入图片描述
    可能有人会说了,看起来AB之间是解耦合了,但是A和队列,B和队列,这不是引入了新的耦合吗?
    诶,这就要说说我们为啥害怕耦合?因为耦合的代码在后续的变更过程中,比较复杂,容易产生bug.
    但是消息队列是成熟稳定的产品,代码不会频繁修改,也就是说代码是稳定的.A和队列,B和队列之间的交互逻辑,基本写一次就固定下来了.

  2. 通过中间的阻塞队列,可以起点"削峰填谷"的作用,在遇到请求量激增的突发情况下,仍可以有效保护下游的服务器,让它们不会被请求冲垮.
    在这里插入图片描述
    到这里,就出现了两个问题:

    1. 为啥一个服务器收到的请求更多,就可能会挂?
      答:一台服务器的配置再好,它的硬件资源也是有限的.服务器每次收到一个请求,在处理这个请求的过程中,就会执行代码,就要消耗一定的硬件资源.当这些请求消耗的总硬件资源的量,超过了服务器能提供的上限,当然就会挂啦.
    2. 在请求激增的时候,A为啥不会挂?队列为啥不会挂?
      答:A的角色是一个"网关服务器",他负责收到客户端的请求,再把请求转发给其他的服务器,这样的服务器里面的代码做的工作比较简单(单纯的数据转发),消耗的硬件资源,通常更少.
      处理一个请求,消耗的资源更少,同样的配置下,就能支持更多的请求处理.
      同理,队列,其实也是比较简单的程序,单位请求消耗的硬件资源,也是比较少的.
      而B这个服务器,是真正干活的服务器,它要真正完成一系列的业务逻辑.这一系列的工作,代码量非常庞大,消耗的时间很多,消耗系统的硬件资源更多,因此更容易挂.

      类似的,MySQL这样的数据库,处理每个请求的时候,做的工作就是比较多的,消耗的硬件资源也是比较多的,因此MySQL也是后端系统中,容易挂的部分.
      对应的,想Redis这种内存数据库,处理请求做的工作远远少于mysql做的工作,因此消耗的资源更少,Redis就比MySQL皮实很多.不容易挂~

代价

  1. 需要更多的机器,来部署这样的消息队列
  2. A和B之间通信的延迟会变长~
    因此,对于A和B之间的调用,如果要求响应的时间比较短,那就不太适合使用了~

BlockingQueue的使用

阻塞队列在Java标准库中提供了现成的封装.
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
Queue的一些操作,offer,poll这些,在BlockingQueue中同样也能够使用(不能阻塞).
BlockingQueue提供了另外两个专属方法(能阻塞):

  1. put 入队列
  2. take 出队列

阻塞队列没有提供"阻塞版本的"获取队首元素的操作.

需要注意的是,put和take产生的阻塞会被interrupt方法唤醒.

自己实现一个阻塞队列(基于数组)

代码:

class MyBlockingQueue {
    private int size = 0;
    private int[] data = null;
    private int head = 0;
    private int tail = 0;

    public MyBlockingQueue(int capacity) {
        data = new int[capacity];
    }

    public void put(int value) throws InterruptedException {
        synchronized (this) {
            while (size == data.length) {
                //阻塞
                this.wait();
            }
            data[tail] = value;
            tail++;
            size++;
            if (tail >= data.length) {
                tail = 0;
            }
            this.notify();
        }
    }

    public int take() throws InterruptedException {
        int ret = 0;
        synchronized (this) {
            while (size == 0) {
                //阻塞
                this.wait();
            }

            ret = data[head];
            head++;
            if (head >= data.length) {
                head = 0;
            }
            size--;
            this.notify();
        }

        return ret;
    }

}

public class Demo18 {

    public static void main(String[] args) {
        MyBlockingQueue myBlockingQueue = new MyBlockingQueue(1000);
        Thread t1 = new Thread(() -> {
            int n = 1;
            while (true) {
                try {
                    myBlockingQueue.put(n);
                    System.out.println("生产:" + n);
                    n++;
                    //Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        Thread t2 = new Thread(() -> {
            while (true) {
                try {
                    int n = myBlockingQueue.take();
                    System.out.println("消费:" + n);
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        t1.start();
        t2.start();

    }
}

一个小知识:
在这里插入图片描述
在这里插入图片描述
while的作用,就是在wait被唤醒之后,再次确认条件,看是否能继续执行.
在这里插入图片描述
注释里也说了,最好把wait放在一个循环中.

本文到这里就结束啦~

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

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

相关文章

EDA投资前沿 | IDAS 2024设计自动化产业峰会之产业投资分论坛前瞻

主持人简介 冯锦锋 上海兴橙资本合伙人/上海市集成电路行业协会 嘉宾介绍:江苏靖江人,上海兴橙资本合伙人,兼任上海集成电路行业协会副秘书长,著有著作《一砂一世界》、《芯路》、《芯镜》。先后获得清华大学管理信息系统工学学…

如何在大模型落地过程中使用高级 RAG 技术?

一、高级RAG概述 基本 RAG 的工作流程可分为三个步骤:索引、检索和生成。在索引阶段,文本被转换为嵌入,然后存储在向量数据库中以创建可搜索的索引。在检索步骤中,用户的查询也被转换为嵌入,此嵌入用于在向量数据库中…

《向量数据库指南》——Milvus赋能博世智能驾驶:数据引领未来出行革命

在深入探讨Milvus在智能驾驶领域的无限潜力时,我们不得不首先认识到,随着自动驾驶技术的飞速发展,数据已成为驱动这一行业变革的核心要素。自动驾驶系统需要处理海量的实时数据,包括但不限于车辆传感器收集的环境信息(如摄像头捕捉的图像、激光雷达的点云数据、雷达探测的…

VTK随笔六:VTK图像处理(图像创建、图像显示)

一、VTK图像创建 1、VTK 图像数据结构 数字图像文件内容由两个部分组成:图像头信息和数据。图像头信息定义了图像的基本信息,主要包括起点位置(Origin)、像素间隔(Space)和维数(Dimension)。通过这三个参数即可确定图像空间位置和大小。 图像数据即为图像像素的像素…

续航更进阶 长安马自达MAZDA EZ-6成功挑战1301公里续航

继在中国“热极”新疆吐鲁番完成高温试炼后,8月24日-26日,长安马自达MAZDA EZ-6(以下称EZ-6)“众测先享官—续航更进阶”再次从兰州出发,EZ-6增程车型以一次充电、一箱油(45L油箱)创造了CLTC工况…

微信小程序主体变更(迁移)法人无法配合扫脸怎么办?

小程序主体变更是指公众平台提供的,协助小程序帐号开发者将其小程序项下业务交由其他开发者的小程序承接、运营的功能和服务。主体变更完成后,小程序的运营权限、主体信息将发生变化。 那么,小程序怎么变更主体信息呢?法人因种种…

游戏开发设计模式之原型模式

目录 原型模式的实现步骤 原型模式的优点 原型模式的应用场景 总结 原型模式在游戏开发中的具体应用案例是什么? 如何在不同编程语言中实现原型模式? Java C# Python C JavaScript 原型模式与其他创建型设计模式(如建造者模式、适…

喂饭级教程!零代码搭建本地个人知识库 ,支持GPT4、Llama3、Kimi等十几种大模型

这篇文章是关于搭建本地个人知识库,零代码!喂饭级教程!支持GPT、Llama3、Kimi等十几种大模型。教程由我编写,每一步已经过验证,可实践! 1 搭建本地知识库优势 部署本地知识库,可以借助大模型能…

国内十大企业薪酬管理咨询公司

思博咨询专注于制造业管理咨询落地辅导。提供战略落地、营销体系、组织体系、薪酬绩效、供应链、精益生产、降本增效、工厂规划等管理咨询服务。 在当今这个竞争激烈的市场环境中,企业薪酬管理已不再是简单的工资发放与调整,而是成为了企业战略的重要组成…

大模型学习成长路径:五个阶段晋级指南,你在哪一级?

第一阶段 不知道概念 第一阶段,「不知道大模型是什么意思」,不知道langchain是什么,不知道llm是什么,不知道文心一言,不知道openAI,不知道prompt是什么? 这个阶段就是疯狂百度,像一…

23 预编译详解

目录 一、预定义符号 二、#define定义常量 三、#define定义宏 四、带有副作用的宏参数 五、宏替换的规则 六、宏函数的对比 七、#和## (一)#运算符 (二)##运算符 八、命名约定 九、#undef 十、命令行定义 十一、条件编…

TCP协议中的可靠性机制

目录 确认应答 滑动窗口 快重传 流量控制 窗口探测 拥塞控制 延迟应答 捎带应答 总结 相较于UDP协议,TCP协议由于要确保通信过程中的可靠性与尽可能提高通信效率提供了很多可靠性机制,因此TCP比较复杂。 确认应答 滑动窗口 滑动窗口是发送方/接…

Jmeter执行多机联合负载

1、注意事项,负载机必须要安装jre,控制机则必须安装jdk。要配置同网段ip,双向关闭防火墙。 每个负载机要平均承担线程数。 具体执行事项查看上面截图所示,控制机和负载机配置。 2、先给负载机设置ip地址,保持与控制…

网络安全新视角:人工智能在防御中的最新应用

人工智能在网络安全中的最新应用 概述 人工智能(AI)在网络安全领域的应用正日益成熟,它通过机器学习和深度学习技术,为网络安全带来了革命性的变革。AI技术不仅能够自动化、智能化地检测、分析和应对安全威胁,还能够…

Transformer-BiLSTM神经网络多输入单输出回归预测的MATLAB实现

在现代人工智能和机器学习领域,深度学习模型已经成为解决复杂问题的重要工具。Transformer和双向长短期记忆网络(BiLSTM)是两种非常强大的神经网络架构,它们在自然语言处理、时间序列预测、图像处理等多个领域表现出色。本文将介绍…

黑马JavaWeb企业级开发(知识清单)07——Ajax、Axios请求、前后端分离开发介绍、Yapi配置步骤

文章目录 前言一、Ajax1. 概述2. 作用3. 同步异步4. 原生Ajax请求(了解即可)5. Axios(重点)5.1 基本使用5.2 Axios别名(简化书写) 二、前后端分离开发1. 介绍1.1 前后台混合开发1.2 前后台分离开发方式&…

ChatGPT真的那么牛吗?

ChatGPT 很受欢迎,主要因为它在很多任务上表现出色,比如回答问题、写作、编程辅助等等。它的强大之处在于它可以理解和生成与上下文相关的自然语言文本,使得它在许多领域中都有用武之地。 和咱国内的文心一言一比较比较就知道了 不抖机灵&…

史上最全软件测试面试题集(含答案),进大厂涨薪必备,赶紧收藏

前阵子一位读者告诉我,某位大厂HR给他发了我之前做的面试题答案合集。 这个消息让我开心了一整天,因为这说明我之前做的面试题系列真的能帮助到部分测试同学,也算是侧面得到了一种认可吧。 今天写的这份面试题我之前就整理分享过&#xff0…

HTB-Explosion(rdp连接)和preignition(目录遍历)

前言 各位师傅大家好,我是qmx_07,今天给大家讲解Explosion靶机 - Explosion 渗透过程 信息搜集 发现服务器开起了3389端口远程服务 远程连接rdp服务 xfreerdp /v:10.129.172.157 /u:Administrator /p: /v 主机名 /u 用户名 /p密码 这篇靶机是对rdp服…

问题记录:树莓派3B+安装OpenMediaVault(OMV)后无WiFi连接处理

目录 实验环境参考教程安装前直接避免出现该问题的方法问题:安装完OpenMediaVault后,此前已配置好的WiFi,无法正常连接解决方法 OpenMediaVault 登录 实验环境 时间:2024年08月27日 硬件:树莓派3B 系统:Ra…