【Java系列】多线程案例学习——基于阻塞队列实现生产者消费者模型

个人主页:兜里有颗棉花糖
欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创
收录于专栏【Java系列专栏】【JaveEE学习专栏】
本专栏旨在分享学习JavaEE的一点学习心得,欢迎大家在评论区交流讨论💌
在这里插入图片描述

目录

  • 一、阻塞式队列
  • 二、生产者消费者模型
    • 生产消费者模型的优势
  • 三、生产者消费者举例代码(基于阻塞队列)
  • 四、基于阻塞式队列实现生产者消费者模型

一、阻塞式队列

什么是阻塞式队列(有两点):

  • 第一点:当队列满的时候,如果此时入队列的话就会出现阻塞,直到其它线程从队列中取走元素为止。
  • 第二点:当队列为空的时候,如果继续出队列,此时就会出现阻塞,一直阻塞到其它线程往队列中添加元素为止。

二、生产者消费者模型

什么是生产者消费者模型:
生产者消费者模型是常见的多线程编程模型,可以用来解决生产者和消费者之间的数据交互问题。

阻塞队列的最主要的一个目的之一就是实现生产者消费者模型(基于阻塞队列实现),生产者消费主模型是处理多线程问题的一种方式。

生产消费者模型的优势

生产者消费主模型的优势:针对分布式系统有两个优势,一个是解耦合(耦合我们可以理解为依赖程度)、另一个是削峰填谷

  • 解耦合:生产者和消费主之间通过缓冲区进行解耦合,而不会对彼此产生直接的依赖,我们通过引入生产者消费者模型(即阻塞队列)就可以达到解耦合的效果,但是付出的代价就是效率有所降低。

  • 削峰填谷:服务器接收到的来自用户端的请求数量可能会因为一些突发时间而暴增,此时服务器面临的压力就非常大了。我们要知道一台服务器承担的上限是一样的,不同的服务器所能承担的上限又是不同的。(机器的硬件资源(CPU、内存、硬盘、网络带宽等等)是有限的,而服务器每处理一个请求都需要消耗一定的资源,请求足够多直到机器的硬件资源招架不住的时候服务器也就挂了)通过引入生产消费者模型(即阻塞队列)就可以起到一个缓冲的作用,其中阻塞队列就承担了服务器的一部分压力,然后当峰值消退的时候,服务器接收到的请求就相对较少了,此时服务器由于阻塞队列的原因依然可以按照既定的顺序处理请求。

  • ‘’

阻塞队列只是一个数据结构,如果我们把这个数据结构单独实现称了一个服务器程序,并且使用单独的主机或者主机群来进行部署的话,此时阻塞式队列就进化成了消息队列。而在Java标准库中已经实现了阻塞队列,并且实现了三种阻塞队列的实现方式:

三、生产者消费者举例代码(基于阻塞队列)

生产消费者模型代码如下(基于阻塞式队列):

import java.util.concurrent.BlockingQueue;import java.util.concurrent.LinkedBlockingQueue;// 生产消费者模型——阻塞队列
public class Demo20 {public static void main(String[] args) {// 创建一个阻塞队列来作为交易场所BlockingQueue<Integer> queue = new LinkedBlockingQueue<>(10);Thread t1 = new Thread(() -> {int count = 0;while(true) {try {queue.put(count);System.out.println("生产元素:" + count);count++;Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});Thread t2 = new Thread(() -> {while(true) {while(true) {try {Integer n = queue.take();System.out.println("消费元素:" + n);} catch (InterruptedException e) {e.printStackTrace();}}}});t1.start();t2.start();}
}

代码运行结果如下:
在这里插入图片描述

四、基于阻塞式队列实现生产者消费者模型

现在,我们自己来基于循环队列来实现阻塞式队列。注意我们这里实现的阻塞队列是基于数组、基于循环队列的阻塞队列。

我们在实现阻塞队列的时候有以下几点需要注意:

  • 线程安全问题:需要给put方法take()方法进行加锁操作。
  • 经过加锁之后还需要考虑到内存可见性问题,这里就涉及到volatile关键字的使用。
  • 阻塞状态以及阻塞状态的解除时机要把握好(即wait()方法notify()方法的使用)。
  • wait()方法不一定是被notify()方法唤醒的,还有可能是被interrupt()方法唤醒的:如果interrupt方法是按照try catch的形式来进行编写的,一旦interrupt方法唤醒wait方法,接着执行完catch之后,代码并不会结束而是继续往后执行,此时就会出现覆盖元素的问题。(解决方法,使用while循环不断等待和检查条件。如果不使用 while 循环在状态被满足之前不断地等待和检查条件,就有可能在 wait 方法返回之后仍然不能安全地进行操作,这可能导致程序出现异常和错误。强烈建议使用wait方法的时候搭配while循环来判定条件

代码如下:

class MyBlockQueue {// 使用string类型的数组来保存元素,我们假设这里只存stringprivate String[] items = new String[1000];//head表示指向队列的头部volatile private int head = 0;volatile private int tail = 0;volatile private int size = 0; // size表示元素个数private Object locker = new Object();public void put(String elem) throws InterruptedException {synchronized(locker) {while(size >= items.length) {//队列已满locker.wait();//return;}items[tail] = elem;tail++;if(tail >= items.length) {tail = 0;}//tail++和下面的if判断可以替换成tail = (tail + 1) % (items.length)//但是站在CPU的角度来看,其实还是简单的if判断比较快size++;locker.notify(); // 用来唤醒队列为空的阻塞情况}}//出队列public String take() throws InterruptedException {synchronized(locker) {while(size == 0) {locker.wait();}String elem = items[head];head++;if(head >= items.length) {head = 0;}size--;//使用notify来唤醒队列阻塞满的情况locker.notify();return elem;}}
}public class Demo21 {public static void main(String[] args) {// 创建两个线程分别表示消费者和生产者MyBlockQueue queue = new MyBlockQueue();Thread t1 = new Thread(() -> {int count = 0;while(true) {try {queue.put(count + "");System.out.println("生产元素: " + count);count++;} catch (InterruptedException e) {e.printStackTrace();}}});Thread t2 = new Thread(() -> {while(true) {try {String count = queue.take();System.out.println("消费元素: " + count);Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t1.start();t2.start();}
}

本文到这里就结束了,希望友友们可以支持一下一键三连哈。嗯,就到这里吧,再见啦!!!

在这里插入图片描述

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

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

相关文章

计算机中找不到vcruntime140.dll无法启动此程序怎么解决?

无法继续执行代码&#xff0c;因为找不到vcruntime140.dll”。那么&#xff0c;vcruntime140.dll是什么文件&#xff1f;它的作用是什么&#xff1f;当它丢失时会对电脑产生什么影响&#xff1f;本文将为您详细介绍vcruntime140.dll文件的相关知识&#xff0c;并提供五种解决vc…

2024年【低压电工】试题及解析及低压电工模拟考试

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 低压电工试题及解析参考答案及低压电工考试试题解析是安全生产模拟考试一点通题库老师及低压电工操作证已考过的学员汇总&#xff0c;相对有效帮助低压电工模拟考试学员顺利通过考试。 1、【单选题】()仪表可直接用于…

Linux 查看系统类型和版本(内核版本 | 发行版本)

Linux 查看系统类型和版本 首先普及下linux系统的版本内容1. 查看linux系统内核版本2. 查看linux系统发行版本 首先普及下linux系统的版本内容 内核版本和发行版本区别 内核版本就是指 Linux 中最基层的代码&#xff0c;版本号如 Linux version 3.10.0-327.22.2.el7.x86_64发行…

项目经理面试10问

今天我们来说说项目经理专业面试的十条经验总结。如果你认真阅读并思考&#xff0c;相信对在屏幕前的你会有所帮助和启发。 1、请做一下自我介绍 自我介绍很重要。无论面试什么岗位&#xff0c;面试官通常都会问你一个最常见的问题&#xff1a;“请做一下自我介绍。” 在准备…

信号与线性系统翻转课堂笔记15——离散LTI系统模型分析

信号与线性系统翻转课堂笔记15——离散LTI系统模型分析 The Flipped Classroom15 of Signals and Linear Systems 对应教材&#xff1a;《信号与线性系统分析&#xff08;第五版&#xff09;》高等教育出版社&#xff0c;吴大正著 一、要点 &#xff08;1&#xff0c;重点&…

如何为前端编写单元测试?从这篇入门指南开始学习!

前言 对于现在的前端工程&#xff0c;一个标准完整的项目&#xff0c;通常情况单元测试是非常必要的。但很多时候我们只是完成了项目而忽略了项目测试。我认为其中一个很大的原因是很多人对单元测试认知不够&#xff0c;因此我写了这边文章&#xff0c;一方面期望通过这篇文章…

HPM6750开发笔记《第一个helloworld例程》

HPM_SDK的使用&#xff1a; HPM_SDK界面如下图 此处选择所支持的5款evk大家根据自己的板子选 此处选择想看的例程工程 此处可选择生成工程的类型 其中debug工程是在纯RAM中运行的&#xff0c;板子掉电后代码会被删除&#xff0c;用来测试比较合适 其中挂flash的工程有两种其中…

java设计模式学习之【解释器模式】

文章目录 引言解释器模式简介定义与用途实现方式 使用场景优势与劣势在Spring框架中的应用表达式解析示例代码地址 引言 在我们的日常生活中&#xff0c;语言的翻译和理解是沟通的关键。每种语言都有自己的语法规则&#xff0c;而翻译人员和计算机程序需要理解并遵循这些规则来…

【将G2O库使用交叉编译移植到arm平台】

一 准备材料 1.下载好g2o的代码。下载地址&#xff1a;https://github.com/RainerKuemmerle/g2o 如果只是在Ubuntu系统上安装g2o&#xff0c;可以参考代码库中的readme.md。 2.下载suitesparse4.4.6. 选择4.4.6版本是因为我发现ROS系统中使用的是这个版本。即使用sudo apt-get …

【Vulnhub 靶场】【Looz: 1】【简单】【20210802】

1、环境介绍 靶场介绍&#xff1a;https://www.vulnhub.com/entry/looz-1,732/ 靶场下载&#xff1a;https://download.vulnhub.com/looz/Looz.zip 靶场难度&#xff1a;简单 发布日期&#xff1a;2021年08月02日 文件大小&#xff1a;2.1 GB 靶场作者&#xff1a;mhz_cyber &…

c语言:输出范围内的质数|练习题

一、题目 输入一个数n&#xff0c;输出n之内的所有质数 如图&#xff1a; 二、思路分析 1、设置一个数num&#xff0c;从2开始&#xff0c;不断作1操作&#xff0c;作为被除数 2、用一个不断自1的数&#xff0c;除以num&#xff0c;如果num不能被整除&#xff0c;则为质数 3、例…

超分之SRGAN

Photo-Realistic Single Image Super-Resolution Using a Generative Adversarial Network使用生成对抗网络的逼真单图像超分辨率一作&#xff1a;Christian Ledig是Twitter2017年的一篇论文。 文章目录 0. 摘要1. 引言1.1 相关工作1.1.1 介绍了SR技术的发展历程1.1.2 介绍了SR…

算法基础day2

前缀和 #include <iostream> using namespace std; const int N100010; int n,m; int a[N],s[N]; int main() {scanf("%d%d",&n,&m);for(int i1;i<n;i) scanf("%d",&a[i]);for(int i1;i<n;i) s[i]s[i-1]a[i];while(m--){int l,r;s…

模型系列:增益模型Uplift Modeling原理和案例

模型系列&#xff1a;增益模型Uplift Modeling原理和案例 目录 1. 简介1. 第一步2. 指标3. 元学习器 3.1 S-学习器3.2 T-学习器3.3 T-学习器相关模型 简介 Uplift是一种用于用户级别的治疗增量效应估计的预测建模技术。每家公司都希望增加自己的利润&#xff0c;而其中一个…

Python+OpenCV 零基础学习笔记(6):ROI

文章目录 相关链接运行环境前言ROI颜色区域分割颜色通道合并 相关链接 【2022B站最好的OpenCV课程推荐】OpenCV从入门到实战 全套课程 CSDN标题里个括号对应视频的分P OpenCVPython CSDN专栏 Gitee 项目地址 运行环境 Python:3.11.5Anaconda:23.7.4IDE:vscode运行环境&#x…

链表:如何利用“假头,新指针,双指针”解决链表问题

Java学习面试指南&#xff1a;https://javaxiaobear.cn 链表是一种线性数据结构&#xff0c;其中的每个元素实际上是一个单独的对象&#xff0c;而所有对象都通过每个元素中的引用字段链接在一起。 链表是一种物理存储单元上非连续、非顺序的存储结构&#xff0c;其物理结构不能…

深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第六节 理解垃圾回收GC,提搞程序性能

深入浅出图解C#堆与栈 C# Heaping VS Stacking 第六节 理解垃圾回收GC&#xff0c;提搞程序性能 [深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第一节 理解堆与栈](https://mp.csdn.net/mdeditor/101021023)[深入浅出图解C#堆与栈 C# Heap(ing) VS Stack(ing) 第二节 栈基…

【kubernetes】集群网络(一):基础篇

Flannel 1 路由表 & arp & fdb 1.1 路由表 任何网络设备都需要路由表&#xff0c;路由表用来决定&#xff0c;当收到数据包时&#xff0c;该向哪里进行转发。路由表项通常会包含以下几个字段&#xff1a; Destination&#xff1a;目的地Gateway&#xff1a;网关Mas…

12.27重构二叉树,插入排序,队列(股票,模拟),后缀表达式求值,括号匹配,验证栈序列,选择题部分

重构二叉树 误 string in, post; struct node {char a;node* lchild, * rchild;node(char x\0) :a(x), lchild(nullptr), rchild(nullptr) {} }; void so(node* r, int il, int ir, int pl, int pr) {if (il > ir)return;int root;for (root il; root < ir; root) {if…

[AI编程]AI辅助编程助手-亚马逊AI 编程助手 Amazon CodeWhisperer

亚马逊AI 编程助手 Amazon CodeWhisperer 是一种基于人工智能技术的编程辅助工具&#xff0c;旨在帮助开发人员更高效地编写代码。它可以提供实时的代码建议、自动补全和错误检查&#xff0c;帮助优化代码质量和提高编程效率。 Amazon CodeWhisperer 使用了自然语言处理和机器…