javaEE初阶——多线程(五)

news2024/11/18 3:23:53

在这里插入图片描述

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.coloradmin.cn/o/1610642.html

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

相关文章

网站空间的类型包括

网站空间的类型包括许多不同的形式&#xff0c;每种形式都具有其独特的特点和用途。从个人博客到企业网站&#xff0c;从电子商务平台到社交网络&#xff0c;各种类型的网站都为用户提供了不同的体验和功能。在本文中&#xff0c;我们将探讨几种常见的网站空间类型&#xff0c;…

MYSQL之增删改查(中)

前言&#xff1a; 以下是MySQL最基本的增删改查语句&#xff0c;很多IT工作者都必须要会的命令&#xff0c;也 是IT行业面试最常考的知识点&#xff0c;由于是入门级基础命令&#xff0c;所有所有操作都建立在单表 上&#xff0c;未涉及多表操作。 4、“查”——之单表查询 My…

Linux的firewalld防火墙

介绍firewalld&#xff1a; ①、firewalld&#xff08;Dynamic Firewall Manager of Linux systems&#xff0c;Linux系统的动态防火墙管理器&#xff09;服务是默认的防火墙配置管理工具&#xff0c;它拥有基于CLI&#xff08;命令行界面&#xff09;和基于GUI&#xff08;图…

专业清洁工匠服务网站模板 html网站

目录 一.前言 二.页面展示 三.下载链接 一.前言 该HTML代码生成了一个网页&#xff0c;包括以下内容&#xff1a; 头部信息&#xff1a;指定了网页的基本设置和元数据&#xff0c;例如字符编码、视口大小等。CSS文件&#xff1a;引入了多个CSS文件&#xff0c;用于设置网页…

程序员自由创业周记#32:新产品构思

程序员自由创业周记#32&#xff1a;新产品构思 新作品 我时常把自己看做一位木匠&#xff0c;有点手艺&#xff0c;能做一些作品养活自己。而 加一、Island Widgets、Nap 就是我的作品。 接下来在持续维护迭代的同时&#xff0c;要开启下一个作品的创造了。 其实早在2022的1…

【leetcode面试经典150题】64. 删除排序链表中的重复元素 II(C++)

【leetcode面试经典150题】专栏系列将为准备暑期实习生以及秋招的同学们提高在面试时的经典面试算法题的思路和想法。本专栏将以一题多解和精简算法思路为主&#xff0c;题解使用C语言。&#xff08;若有使用其他语言的同学也可了解题解思路&#xff0c;本质上语法内容一致&…

C++必修:从C语言到C++的过渡(上)

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;C学习 贝蒂的主页&#xff1a;Betty’s blog 1. 什么是C C&#xff08;c plus plus&#xff09;是一种计算机高级程序设计语言&…

链表经典算法OJ题目

1.单链表相关经典算OJ题目1&#xff1a;移除链表元素 思路一 直接在原链表里删除val元素&#xff0c;然后让val前一个结点和后一个节点连接起来。 这时我们就需要3个指针来遍历链表&#xff1a; pcur —— 判断节点的val值是否于给定删除的val值相等 prev ——保存pcur的前…

【详细讲解下Photoshop】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

OpenHarmony 网络管理-Socket连接

介绍 本示例主要演示了Socket在网络通信方面的应用&#xff0c;展示了Socket在两端设备的连接验证、聊天通信方面的应用。 效果预览 使用说明 1.搭建服务器环境&#xff1a;修改服务器脚本中的服务端IP地址&#xff0c;与本机IP地址保持一致&#xff0c;修改完成后双击运行脚…

手撸词法分析器(C/C++)

手撸词法分析器&#xff08;C/C&#xff09; 一.背景二.什么是词法分析器&#xff1f;三.代码四.思考 一.背景 这学期开设了编译原理&#xff0c;要求写个基本的词法分析器。所以博主就自己写了一份代码&#xff0c;也比较简单基础。 二.什么是词法分析器&#xff1f; 简单来…

Unity实现动态数字变化

最近的项目需要动态显示数字&#xff0c;所以使用Text组件&#xff0c;将数字进行变化操作过程记录下来。 一、UI准备 1、新建一个Text组件 2、新建C#脚本 3、将Text挂载到脚本上 二、函数说明 1、NumberChange 方法 NumberChange 方法接收四个参数&#xff1a;初始数字 in…

设备连接IoT云平台指南

一、简介 设备与IoT云间的通讯协议包含了MQTT&#xff0c;LwM2M/CoAP&#xff0c;HTTP/HTTP2&#xff0c;Modbus&#xff0c;OPC-UA&#xff0c;OPC-DA。而我们设备端与云端通讯主要用的协议是MQTT。那么设备端与IoT云间是如何创建通信的呢&#xff1f;以连接华为云IoT平台为例…

不容忽视的办公网络安全威胁 零信任或成破局关键

移动互联网、混合云和 SaaS 时代的来临&#xff0c;让企业的办公网络环境发生着巨大变化&#xff0c; BYOD、移动办公以及访问云端 SaaS 应用的场景已经越来越频繁&#xff0c;在方便协作、提升效率的同时&#xff0c;潜在的安全威胁以及管理困境也日益突出。比如&#xff1a; …

快速入门Spring Data JPA

Spring Data JPA是Spring Data框架的一小部分&#xff0c;它能够让开发者能够更加简单的对数据库进行增删改查。 由于Spring Data JPA可以自动生成SQL代码所以一般情况下&#xff0c;简单的增删查改就可以交给Spring Data JPA来完成&#xff0c;而复杂的动态SQL等用MyBatis来完…

免费使用ChatGPT 4.0 和 文心一言 4.0

前言 今天给大家分享如何免费使用ChatGPT4.0 和 文心一言 4.0&#xff0c;废话就不多说了&#xff0c;我们直接入正题。 ChatGPT 4.0 先来看看如何免费使用ChatGPT 4.0 进入Coze登录 https://www.coze.com 选择大圣-GPT-4 文心一言 4.0 通过文心智能体平台&#xff0c;就…

[阅读笔记20][BTX]Branch-Train-MiX: Mixing Expert LLMs into a Mixture-of-Experts LLM

这篇论文是meta在24年3月发表的&#xff0c;它提出的BTX结构融合了BTM和MoE的优点&#xff0c;既能保证各专家模型训练时的高度并行&#xff0c;又是一个统一的单个模型&#xff0c;可以进一步微调。 这篇论文研究了以高效方法训练LLM使其获得各领域专家的能力&#xff0c;例如…

C++智能指针(二十)

一.RAII&#xff08;Resource Acquisition Is Initialization&#xff09; RAII资源获取即初始化&#xff0c;RAII的思想就是在构造时初始化资源&#xff0c;或者托管已经构造的资源。在析构的时候释放资源。一般不允许复制或赋值&#xff0c;并且提供若干的资源访问的方法。比…

openGauss学习笔记-265 openGauss性能调优-TPCC性能调优测试指导-操作系统配置

文章目录 openGauss学习笔记-265 openGauss性能调优-TPCC性能调优测试指导-操作系统配置265.1安装openEuler操作系统265.2 修改操作系统内核PAGESIZE为64KB。265.3 关闭CPU中断的服务irqbalance openGauss学习笔记-265 openGauss性能调优-TPCC性能调优测试指导-操作系统配置 本…

oracle一次sql优化笔记

背景&#xff1a;两个百万级数据量表需要连接&#xff0c;加全索引的情况下速度仍不见改善&#xff0c;苦查一下午解决问题未遂。 解决&#xff1a;经大佬指点了解到oracle优化器提示&#xff0c;使用/* USE_HASH(table1 table2) */或者/* USE_MERGE(table1 table2) */来指导优…