【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://www.rhkb.cn/news/226241.html

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

相关文章

ubuntu 在线安装 python3 pip

ubuntu 在线安装 python3 pip 安装 python3 pip sudo apt -y install python3 python3-pip升级 pip python3 -m pip install --upgrade pip

【头歌实训】kafka-入门篇

文章目录 第1关&#xff1a;kafka - 初体验任务描述相关知识Kafka 简述Kafka 应用场景Kafka 架构组件kafka 常用命令 编程要求测试说明答案代码 第2关&#xff1a;生产者 &#xff08;Producer &#xff09; - 简单模式任务描述相关知识Producer 简单模式Producer 的开发步骤Ka…

ROS多机通信

1&#xff1a;安装ssh sudo apt-get install openssh-server ps -e|grep ssh2&#xff1a;网络静态IP设置 3&#xff1a;配置文件修改 sudo gedit /etc/hosts192.168.3.11 用户名 192.168.3.22 用户名另一台4&#xff1a;重启网络 sudo /etc/init.d/network-manager resta…

2023年度业务风险报告:四个新风险趋势

目录 倒票的黄牛愈加疯狂 暴增的恶意网络爬虫 愈加猖獗的羊毛党 层出不穷的新风险 业务风险呈现四个趋势 防御云业务安全情报中心“2023年业务风险数据”统计显示&#xff0c;恶意爬虫风险最多&#xff0c;占总数的37.8%&#xff1b;其次是虚假账号注册&#xff0c;占18.79%&am…

MySQL事务、四大原则、执行步骤、四种隔离级别、锁、脏读、脏写等

MySQL事务 MySQL事务1.什么是事务&#xff1f;2.事务的四大原则3.事务执行的步骤4、事务的隔离性5、MySQL中的锁 MySQL事务 模拟一个转账业务&#xff1a; 上图中的sql语句&#xff1a; update from table set money mongey - 100 where name A; update from table set mone…

RabbitMQ 报错:Failed to declare queue(s):[QD, QA, QB]

实在没想到会犯这种低级错误。 回顾整理一下吧&#xff1a; 原因&#xff1a;SpringBoot主配置类默认只会扫描自己所在的包及其子包下面的组件。其他位置的配置不会被扫描。 如果非要使用其他位置&#xff0c;就需要在启动类上面指定新的扫描位置。注意新的扫描位置会覆盖默…

PHP的Laravel的数据库迁移

1.默认迁移文件 2.数据库迁移 在终端输入以下代码 php artisan migrate 我的报错啦&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 数据库里面只有两张表&#xff0c;实际上应该有四张的&#xff01;&#xff01;&#xff01; 解决方法&#xff1a; 反正表已…

基于动态窗口的航线规划

MATLAB2016b可以运行 % ------------------------------------------------------------------------- % File : DWA 算法 % Discription : Mobile Robot Motion Planning with Dynamic Window Approach % Author :Yuncheng Jiang % License : Modified BSD Software License A…

【JDK21】详解虚拟线程

目录 1.概述 2.虚拟线程是为了解决哪些问题 2.1.线程切换的巨大代价 2.2.哪些情况会造成线程的切换 2.3.线程资源是有限的 3.虚拟线程 4.适用场景 1.概述 你发任你发&#xff0c;我用JAVA8&#xff1f;JDK21可能要对这句话say no了。 现在Oracle JDK是每4个版本&#x…

什么是https证书?

HTTPS证书&#xff0c;也称为SSL&#xff08;Secure Sockets Layer&#xff09;证书或TLS&#xff08;Transport Layer Security&#xff09;证书&#xff0c;是一种数字证书&#xff0c;用于在网络上建立安全的加密连接。它的主要目的是确保在互联网上进行的数据传输的安全性和…

工具系列:TimeGPT_(6)同时预测多个时间序列

TimeGPT提供了一个强大的多系列预测解决方案&#xff0c;它涉及同时分析多个数据系列&#xff0c;而不是单个系列。该工具可以使用广泛的系列进行微调&#xff0c;使您能够根据自己的特定需求或任务来定制模型。 # Import the colab_badge module from the nixtlats.utils pac…

AD使用的一些基本知识

主页工厂打板时&#xff0c;有些过孔要求在0.3/0.5以上&#xff0c;还有其他一些工艺要求也要注意 用keep-out layer还是mechanical layer 当做切割边线&#xff0c;都可以&#xff0c;也可以看制版工厂的要求 导出BOM表时&#xff0c;是以comment分类的&#xff0c;通常情况…

php-ssrf

漏洞描述&#xff1a; SSRF(Server-Side Request Forgery:服务器端请求伪造) 是一种由攻击者构造形成由服务端发起请求的一个安全漏洞。 一般情况下&#xff0c;SSRF攻击的目标是从外网无法访问的内部系统。&#xff08;正是因为它是由服务端发起的&#xff0c;所以它能够请求…

FTP原理与配置

FTP是用来传送文件的协议。使用FTP实现远程文件传输的同时&#xff0c;还可以保证数据传输的可靠性和高效性。 FTP的应用 FTP 提供了一种在服务器和客户机之间上传和下载文件的有效方式。在企业网络中部署一台FTP服务器&#xff0c;将网络设备配置为FTP客户端&#xff0c;则可…

大数据开发之Sqoop详细介绍

测试环境 CDH 6.3.1 Sqoop 1.4.7 一.Sqoop概述 Apache Sqoop&#xff08;SQL-to-Hadoop&#xff09;项目旨在协助RDBMS与Hadoop之间进行高效的大数据交流。用户可以在 Sqoop 的帮助下&#xff0c;轻松地把关系型数据库的数据导入到 Hadoop 与其相关的系统 (如HBase和Hive)中&…

Android : 画布绘制矩形和文字 让其居中显示简单应用

示例图&#xff1a; CenterView.java package com.example.demo;import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.util.Log; import android.view.View;public class Center…

基于ChatGpt,Java,SpringBoot,Vue,Milvus向量数据库的定制化聊天Web demo

customized chat GitHub - bigcyy/customized-chatgpt: 基于ChatGpt&#xff0c;Java&#xff0c;SpringBoot&#xff0c;Vue&#xff0c;Milvus向量数据库的定制化聊天Web demo 简介 基于ChatGpt&#xff0c;Java&#xff0c;SpringBoot&#xff0c;Vue&#xff0c;Milvus向…

华为---登录USG6000V防火墙---console、web、telnet、ssh方式登录

目录 一、环境搭建 二、第一次登录USG6000V防火墙&#xff0c;即通过console方式登录 三、通过web管理界面创建用户 四、web登录USG6000V防火墙 1. 用web创建的用户通过web方式登录USG6000V防火墙 2. 命令行创建的用户通过web方式登录USG6000V防火墙 五、ssh方式登录USG60…

TPRI-DMP平台介绍

TPRI-DMP平台介绍 TPRI-DMP平台概述 TPRI-DMP为华能集团西安热工院自主产权的工业云PaaS平台&#xff0c;已经过13年的发展和迭代&#xff0c;其具备大规模能源电力行业生产应用软件开发和运行能力。提供TPRI-DMP平台主数据管理、业务系统开发与运行、应用资源管理与运维监控…

【C语言】程序练习(二)

大家好&#xff0c;这里是争做图书馆扫地僧的小白。 个人主页&#xff1a;争做图书馆扫地僧的小白_-CSDN博客 目标&#xff1a;希望通过学习技术&#xff0c;期待着改变世界。 目录 前言 一、运算符练习 1 算术运算符 1.1 练习题&#xff1a; 2 自加自减运算符 3 关系运…