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.mfbz.cn/a/560555.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

力扣---填充每个节点的下一个右侧节点指针 II

给定一个二叉树&#xff1a; struct Node {int val;Node *left;Node *right;Node *next; } 填充它的每个 next 指针&#xff0c;让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点&#xff0c;则将 next 指针设置为 NULL 。 初始状态下&#xff0c;所有 next 指针都…

大厂面试精华面试刷题

1.自定义unshift实现相同效果 2.数组去重 用vs2019来写这种练习题可以更直观的查看代码执行的效果&#xff0c;最后的代码是控制控制台执行完毕后不自动关闭 use strict;let arr [1, 1, 2, 2, 3, 3, 4, 5, 6, 7, 8, 9, 10] //1.//查重最简单的方法for循环结合splice从数组中…

win10 系统怎么开启 guest 账户?

win10 系统怎么开启 guest 账户&#xff1f; 段子手168 前言&#xff1a; guest 账户即所谓的来宾账户&#xff0c;我们可以通过该账户访问计算机&#xff0c;如打印机共享等&#xff0c;但会在一定程度上受到限制。下面分享 WIN10 系统开启 guest 来宾账户的几种方法。 方法…

【Transformer】detr梳理

every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 detr detr 1. 引言 论文&#xff1a; https://arxiv.org/pdf/2005.12872v3.pdf 时间&#xff1a; 2020.5.26 作者&#xff1a; Nicolas Carion?, Fra…

SnapGene Mac激活版 分子生物学软件

SnapGene Mac是一款功能全面、操作便捷的综合性分子生物学软件&#xff0c;专为Mac用户打造。它集成了DNA序列编辑、分析、可视化和团队协作等多种功能&#xff0c;为科研人员提供了一个高效、可靠的分子生物学研究工具。 SnapGene Mac激活版下载 在SnapGene Mac中&#xff0c;…

【Hadoop大数据技术】——Sqoop数据迁移(学习笔记)

&#x1f4d6; 前言&#xff1a;在实际开发中&#xff0c;有时候需要将HDFS或Hive上的数据导出到传统关系型数据库中&#xff08;如MySQL、Oracle等&#xff09;&#xff0c;或者将传统关系型数据库中的数据导入到HDFS或Hive上&#xff0c;如果通过人工手动进行数据迁移的话&am…

美容预约小程序:简单三步,开启高效预约模式

在当今的数字化时代&#xff0c;一个小程序可以极大地提高美容院的效率和客户满意度。下面我们将详细说明如何通过以下步骤来搭建一个美容院预约小程序。 首先&#xff0c;你需要注册并登录到乔拓云网&#xff0c;这是 一个在线平台&#xff0c;可以帮助你快速创建并管理你的小…

Kafka集群搭建可视化指南

欢迎来到我的博客&#xff0c;代码的世界里&#xff0c;每一行都是一个故事 Kafka集群搭建可视化指南 前言准备工作硬件要求环境准备 kafka集群的部署与配置3.1 单节点部署与多节点集群搭建单节点部署&#xff1a;多节点集群搭建&#xff1a; 3.2 Broker配置与优化3.3 Topic的创…

快速访问github

修改本地hosts文件 GitHub访问慢的原因在于域名解析&#xff0c;通过修改本地的hosts文件&#xff0c;将远程DNS解析改为本地DNS解析。 fang 步骤1&#xff1a;打开hosts文件&#xff08;没有就创建&#xff09; host所在位置&#xff1a; C:\Windows\System32\drivers\etc…

销售经理(多继承/虚基类)

根据下图类之间的继承关系&#xff0c;以及main和输出定义&#xff0c;定义Staff类、Saleman类、Manager类和SaleManager类。 Staff类包含的数据成员有编号&#xff08;num)&#xff0c;姓名&#xff08;name)&#xff0c;基本工资&#xff08;basicSale&#xff09;。Saleman类…

[lesson42]类型转换函数(下)

类型转换函数(下) 类型转换函数 C类中可以定义类型转换函数 类型转换函数用于将类对象转换成其他类型 语法规则&#xff1a; 类型转换函数 与转换构造函数具有同等的地位使得编译器有能力将对象转换为其他类型编译器能够隐式的使用类型转换函数 无法抑制隐式的类型转换函…

项目实践:贪吃蛇

引言 贪吃蛇作为一项经典的游戏&#xff0c;想必大家应该玩过。贪吃蛇所涉及的知识也不是很难&#xff0c;涉及到一些C语言函数、枚举、结构体、动态内存管理、预处理指令、链表、Win32 API等。这里我会介绍贪吃蛇的一些思路。以及源代码也会给大家放到文章末尾。 我们最终的…

二维码门楼牌管理应用平台建设:取保候审的智能化监管

文章目录 前言一、取保候审的传统监管困境二、二维码门楼牌管理应用平台的优势三、取保候审备案信息的智能化处理四、保障居民合法权益五、展望未来 前言 随着信息技术的飞速发展&#xff0c;二维码门楼牌管理应用平台已成为现代社区治理的重要工具。本文重点探讨如何借助该平…

Vue【路由】

1&#xff1a;什么是单页应用程序&#xff08;single page application&#xff09; 所有得功能在一个html页面上实现 2&#xff1a;单页面应用程序的优缺点 优点&#xff1a;按需更新性能高&#xff0c;开发效率也高&#xff0c;用户的体验较好 缺点&#xff1a;学习成本高…

卷王问卷考试系统/SurveyKing调查系统源码

SurveyKing是一个功能强大的开源调查问卷和考试系统&#xff0c;它能够快速部署并适用于各个行业。 这个系统提供了在线表单设计、数据收集、统计和分析等功能&#xff0c;支持20多种题型&#xff0c;提供多种创建问卷的方式和设置。 项 目 地 址 &#xff1a; runruncode.c…

JavaFX--基础简介(1)

一、介绍 中文官网&#xff1a;JavaFX中文官方网站OpenJFX 是一个开源项目,用于桌面、移动端和嵌入式系统的下一代客户端应用程序平台。openjfx.cn是OpenJFX(JavaFX)的标准中文翻译网站&#xff0c;致力于方便开发者阅读官方文档和教程。https://openjfx.cn/ JavaFX 是一个开…

2024第八届图像、信号处理和通信国际会议 (ICISPC 2024)即将召开!

2024第八届图像、信号处理和通信国际会议 &#xff08;ICISPC 2024&#xff09;将于2024年7月19-21日在日本福冈举行。启迪思维&#xff0c;引领未来&#xff0c;ICISPC 2024的召开&#xff0c;旨在全球专家学者共襄盛举&#xff0c;聚焦图像信号&#xff0c;在图像中寻找美&am…

袁庭新ES系列15节|Elasticsearch客户端基础操作

前言 上一章节我们介绍了搭建Elasticsearch集群相关的知识。那么又该如何来操作Elasticsearch集群呢&#xff1f;在ES官网中提供了各种语言的客户端&#xff0c;我们在项目开发过程中有多种Elasticsearch版本和连接客户端可以选择&#xff0c;那么他们有什么区别&#xff1f;这…

uniapp Android 插件开发教程

一、下载uniapp提供的SDK Android 离线SDK - 正式版 | uni小程序SDK 二、在uniapp创建一个项目 查看包名&#xff1a;发行--> 原生app 云打包 三、进入dcloud官网 开发者中心 进入 应用管理 --> 我的应用 --> 点击应用名称-->各平台信息-->新增 这里需要这…

【可视化大屏开发】19. 加餐-百度地图API实现导航加线路热力图

需求 Web端使用场景中会涉及到地图导航路线情况&#xff0c;并利用热力图显示路况信息。 实现效果如下&#xff1a; 输入起始地点&#xff0c;选择并开始导航 最终效果 思路步骤 利用百度地图API显示地图交通拥堵情况的热力图&#xff0c;需要按照以下步骤进行开发 步骤1&a…
最新文章