阻塞队列-生产者消费者模型

news2024/11/18 1:28:37

    • 阻塞队列介绍
      • 标准库阻塞队列使用
      • 基于阻塞队列的简单生产者消费者模型。
      • 实现一个简单型阻塞队列 (基于数组实现)

阻塞队列介绍

不要和之前学多线程的就绪队列搞混;
在这里插入图片描述
阻塞队列:也是一个队列,先进先出。带有特殊的功能 ;阻塞
1:如果队列为空,执行出队列操作,就会阻塞阻塞到另一个线程往队列里添加元素(队列不空)为止
2:如果队列满了,执行入队列操作,也会阻塞阻塞到另一个线程从队列取走元素位置(队列不满)

消息队列:
先简单介绍消息队列:特殊的队列,相当于在阻塞队列的基础上,加上一个消息类型,按照类别进行先进先出。
因为这个消息队列使用起来太香;所以有大佬把这样的数据结构单独实现成一个程序;可以单独的部署到一组服务器上。使储存、转发能力大大提升。现在已经发展可以和mysql、redis相提并论的一个重要组件。
消息队列之所以这么好用和阻塞队列阻塞特性关系非常大;基于这种特性可以实现 “生产者消费者模型”

生产者消费者模型(常用的并发设计模式):比如包饺子;一个人负责制作饺子皮;一个人负责包;一个人负责下锅。不会说每个人单独包一个饺子,这样会导致撵面杖不够用,影响效率。两个好处。
1:实现发送方与接收方之间的解耦。
比如下面这种是耦合度比较高;A要调用B(A把请求转发给B处理,B把结果反馈给A),A得知道B的存在。如果B挂了,很容易引起A的bug。。如果你要增加一个C服务器,对A也需要修改代码,对A又得重新测试等又不知道是否改动A会对B有影响。
在这里插入图片描述

在这里插入图片描述
这种模型下;A、B耦合就降低很多;A中无B,B中无A的代码。两边有一方挂了都互相没任何影响;因为阻塞队列是正常的。B挂了,A仍然可以插入元素,满了就阻塞。A挂了,B仍然可以从队列获取元素;空了就阻塞。增加一个C服务器对A是无感知的。

2:削峰填谷的作用,保证系统的稳定性。
服务器开发也很类似;说不定坤哥唱一首只因你太美;热搜瞬间上来,很多用户给你发请求,如果没有削峰填谷的准备服务器很容易就挂掉。

标准库阻塞队列使用

在这里插入图片描述

Queue提供:入队列 offer 、出队列 poll、取队首元素 peek。
阻塞队列主要方法:入队列 put、出队列 take。抛异常阻塞得需要唤醒,空队列你再取就阻塞。阻塞队列也有offer和poll方法,但是这些是不带阻塞功能的。

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.PriorityBlockingQueue;

public class test14 {//标准库阻塞队列使用
    public static void main(String[] args) throws InterruptedException {
//        BlockingDeque<String> q=new PriorityBlockingQueue<>() 基于堆;带有优先级阻塞队列
        BlockingDeque<String> q=new LinkedBlockingDeque<>();//基于链表实现
//        BlockingDeque<String> q=new ArrayBlockingQueue<>(); 基于数组实现
        q.put("hello");
        System.out.println(q.take());//输出结果hello
        q.take();//空的时候取就会阻塞


    }
}

基于阻塞队列的简单生产者消费者模型。

import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;

public class test15 {//基于阻塞队列生产者消费者模型
    static   int i=0;
    public static void main(String[] args) {
        BlockingDeque<Integer> blockingDeque=new LinkedBlockingDeque();

  Thread t1=new Thread(()->{//生产者
     while (true){
          try {
              blockingDeque.put(i);
              System.out.println(i);//生产的元素
              i++;
              Thread.sleep(500);//生产的很慢;消费者就得阻塞先;等到元素生产出来
          } catch (InterruptedException e) {
              throw new RuntimeException(e);
          }
      }

  });
        t1.start();

        Thread t2=new Thread(()->{//消费者
          while (true){
                try {
                    System.out.println(blockingDeque.take());//消费的元素
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }

        });
        t2.start();
    }
}

执行效果
在这里插入图片描述

实现一个简单型阻塞队列 (基于数组实现)

先复习一下循环队列的实现。

public class test16 {//简单阻塞队列的实现;;先复习一遍循环队列
    private int[] elem;
    public int front;//队头
    public int rear;//队尾

    public test16(int k) {//创建这个对象,就有循环队列k大小数组
        elem=new int[k];
    }


    //判满;入队的前提条件,入队是满就直接返回。或者你在满的时候扩容,入之前判断是否满;如果满扩容再入
    public boolean isfull(){
        //怎么判;队尾+1 回到头。但是刚好队头在原点就会产生;位置相同;但是队尾+1的值不等于0.
        if((rear+1)%elem.length==front){
            return true;

        }
        return false;


    }
    //判空;出队的前提条件,出队是空直接返回
    public boolean isnull(){
 //对头==队就是空
        if(rear==front){
            return true;
        }
return false;

    }

    //入队方法;
public boolean offer1(int i) {
        if(isfull()){//满的情况返回入队失败

            return false;
        }
        //怎么入;是入在后面还是前面呢;队列尾入;这时候浪费的那个空间直接赋值进去;队尾往后移就好了
    elem[rear]=i;//会不会有点不合适;万一前面是没有元素呢
        rear=(rear+1)%elem.length;                  //这个队尾两种情况;第一种是单纯的加1;未到数组最后位置
                                                     //比如我把前面空间删除。刚好到最后一个位置;就得回归原点。%elem.length
                                                  
          //这里分开写效果会更好;rear++; if(rear>=elem.length){ rear=0; }
          //相比之下rear++;read=read%elem.length 。这个不易读,不直观;每次得理解好一会 。效率上又没有优势
                                                                             
        return true;

}




    //出队方法
    public Int poll1(){//头出、怎么出呢。感觉不合理;出队应该返回这个元素.所以得先记录
        if(isnull()){
            return -1;
        }
        int value=front;
     front=(front+1)%elem.length;//往后移一位;elem.length避免逛了一圈
        return elem[value];
    }
    //获取队首
  public int  peek1(){
        if(isnull()){
            return -1;
        }
 return elem[front];

  }
  //获取队尾
  public int  Rear(){
      if(isnull()){
          return -1;
      }
      //得注意;不能直接返回read-1.因为如果read是原点;那么减1不就变成负一
      if(rear==0){
          return elem[elem.length-1];
      }
      else
          return elem[rear-1];


  }

    public static void main(String[] args) {



    }
}

我们在此基础上加上阻塞功能就好了;也就是在多线程环境下使用;得保证线程安全。
在线程里使用这些方法时;如果出现符合阻塞条件情况就阻塞。

public class test17 {
    static int i;
    public static void main(String[] args) throws InterruptedException {
        //看看能不能基于我们创建这个阻塞队列实现生产者消费者模型
      my blockingDeque=new my(100);//这里浪费一个空间;达到98;就会阻塞

        Thread t1=new Thread(()->{//生产者
            while (true){
                try {
                    blockingDeque.offer1(i);
                    System.out.println(i);//生产的元素
                    i++;
                    Thread.sleep(50);//生产的很慢;消费者就得阻塞先;等到元素生产出来
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }

        });
        t1.start();

        Thread t2=new Thread(()->{//消费者
            while (true){
                try {
                    System.out.println(blockingDeque.poll1());//消费的元素
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }

        });
       // t2.start();
    }

    }

class my {//简单阻塞队列的实现;;先复习一遍循环队列
    private int[] elem;
    public int front;//队头
    public int rear;//队尾

    public my(int k) {
        elem = new int[k];
    }


    public void offer1(int i) throws InterruptedException {
        synchronized (this) {
            if (isfull()) {
                this.wait();
            }

            elem[rear] = i;
            rear = (rear + 1) % elem.length;
            // 这个 notify 唤醒 poll1 中的 wait
            this.notify();
        }

    }


    //出队方法
    public int poll1() throws InterruptedException {
        synchronized (this) {
            if (isnull()) {
                this.wait();//两个wait是不可能同时触发的;因为一个队列不可能既是空又是满。只要this是同一个对象就不会.
            }

            int value = front;
            front = (front + 1) % elem.length;
            // 这个 notify 唤醒 offer1 中的 wait
            this.notify();
            return elem[value];
        }
    }
    public boolean isfull() {
        if ((rear + 1) % elem.length ==front) {
            return true;

        }
        return false;


    }

    public boolean isnull() {
        //队头==队尾就是空
        if (rear == front) {
            return true;
        }
        return false;

    }

}

上述代码还有一丝丝的瑕疵;offer1中wait被唤醒的时候;if的条件一定就不成立?(也就是队列一定是不满的?)
我们当前代码是取元素的时候;就唤醒,是不存在这个问题。万一其它操作也可能唤醒这个wait;但是情况又是队列不满的呢?
所以我们改成

whlie (isnull()) {
                this.wait();
            }

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

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

相关文章

LeetCode_二叉树_中等_1372.二叉树中的最长交错路径

目录 1.题目2.思路3.代码实现&#xff08;Java&#xff09; 1.题目 给你一棵以 root 为根的二叉树&#xff0c;二叉树中的交错路径定义如下&#xff1a; 选择二叉树中 任意 节点和一个方向&#xff08;左或者右&#xff09;。如果前进方向为右&#xff0c;那么移动到当前节点…

基于Python + SnowNLP实现一个文本情感分析系统

当你浏览社交媒体、新闻或任何数字内容时&#xff0c;你有没有想过背后的技术是如何分析和理解这些文本的情感的&#xff1f;有没有想过在数百万条评论、帖子或文章中&#xff0c;如何快速地识别出其中的积极和消极情绪&#xff1f;在这篇文章中&#xff0c;我们将揭示其中的奥…

数据结构—堆(C语言实现)

目录 堆是什么&#xff1f; 一、大堆 一、小堆 如何实现堆&#xff1f; 代码实现 &#xff1f; 一、定义堆的结构体 二、初始化堆 三、构建堆 1.利用向下调整算法 2.开始构建 四、插入元素 1.利用向上调整算法 五、取出堆顶元素、销毁堆 六、堆排序 Extra&#…

SpringBoot中xml映射文件

1.规范 说明&#xff1a;XML映射文件的名称与Mapper接口名称一致&#xff0c;并且将XML映射文件和Mapper接口放置在相同包下&#xff08;同包同名&#xff09;。 xML映射文件的namespace属性为Mapper接口全类名一致。 XML映射文件中sql语句的id与Mapper接口中的方法名一致&…

Spring面试题4:面试官:说一说Spring由哪些模块组成?说一说JDBC和DAO之间的联系和区别?

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:说一说Spring由哪些模块组成? Spring是一个开源的Java框架,由多个模块组成,每个模块都提供不同的功能和特性。下面是Spring框架的主要模块: S…

基于nodejs+vue办公OA公文发文件管理系统

论文的研究内容包括&#xff1a;公文分类、公文信息、待办提醒等方面进行了研究。系统以当前应用最为广泛的nodejs语言为基础&#xff0c;结合了目前应用最为广泛的嵌入式嵌入式平台&#xff0c;集成了B/S体系结构。数据库选择简便高效的MySQL&#xff0c;vue框架。在OA公文发文…

大语言模型之十 SentencePiece

Tokenizer 诸如GPT-3/4以及LlaMA/LlaMA2大语言模型都采用了token的作为模型的输入输出&#xff0c;其输入是文本&#xff0c;然后将文本转为token&#xff08;正整数&#xff09;&#xff0c;然后从一串token&#xff08;对应于文本&#xff09;预测下一个token。 进入OpenAI官…

【LeetCode75】第六十三题 不同路径

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 题目给我们返回地图的长和宽。问我们从地图的左上走到右下有几种方法。我们只能往下走或是往右走。 这个算是简单的二维动态规划题了。 …

Docker安装 MySQL8.0.33

Docker 核心&#xff1a;容器 Container 和镜像 Images Docker 安装镜像 mysql:8.0.33 图形化安装界面可选择版本较少 推荐使用命令行安装(可以自定义安装的版本) 版本安装命令链接&#xff1a;mysql Tags | Docker Hub 终端执行命令 docker pull mysql:8.0.33创建容器 安装完…

多维时序 | MATLAB实现WOA-CNN-BiGRU-Attention多变量时间序列预测(SE注意力机制)

多维时序 | MATLAB实现WOA-CNN-BiGRU-Attention多变量时间序列预测&#xff08;SE注意力机制&#xff09; 目录 多维时序 | MATLAB实现WOA-CNN-BiGRU-Attention多变量时间序列预测&#xff08;SE注意力机制&#xff09;预测效果基本描述模型描述程序设计参考资料 预测效果 基本…

【Hello Linux】高级IO Select

本篇博客介绍&#xff1a;介绍Linux中的高级IO 高级IO IO五种IO模型非阻塞IO如何判断异常读取 IO多路转接之Selectselect的优缺点 网络通信的本质其实就是一种IO 而我们知道的是 IO的效率实际上是十分低下的 所以说我们需要一些机制或者方法来解决这个效率问题 IO IO实际上…

R and RStudio的安装教程【2023】

首先需要安装R&#xff0c;才能安装RStudio。 安装包文末获取或者去官网获取&#xff0c;一样的&#xff1a; R的安装&#xff1a;&#xff08;很简单&#xff0c;如果你想安装到C盘&#xff0c;全部选项无脑选下一步&#xff0c;不想安装到C盘&#xff0c;就改一下就ok.&…

软件测试同行评审到底是什么?

【软件测试面试突击班】如何逼自己一周刷完软件测试八股文教程&#xff0c;刷完面试就稳了&#xff0c;你也可以当高薪软件测试工程师&#xff08;自动化测试&#xff09; “同行评审是一种通过作者的同行(开发、测试、QA等)来确认缺陷和需要变更区域的检查方法。”在软件测试中…

五、C#—字符串

&#x1f33b;&#x1f33b; 目录 一、字符串1.1 字符类型1.2 转义字符1.3 字符串的声明及赋值1.3.1 c# 中的字符串1.3.2 声明字符串1.3.3 使用字符串1.3.4 字符串的初始化1.3.4.1 引用字符串常量之初始化1.3.4.2 利用字符数组初始化1.3.4.3 提取数组中的一部分进行初始化 1.3.…

企业微信自建小程序应用踩坑实践

最近开发了一个小程序接入企业微信的需求&#xff0c;企业微信的权限限制诸多&#xff0c;网上的完整示例又少之又少&#xff0c;因此踩了比较多坑&#xff0c;与大家分享。 开发调试 在开发者工具中如果直接使用微信小程序模式&#xff0c;调用wx.qy接口会提示不存在&#x…

Qt5开发及实例V2.0-第二十一章-Qt.Quick Controls开发基础

Qt5开发及实例V2.0-第二十一章-Qt.Quick Controls开发基础 第21章 Qt Quick Controls开发基础21.1 Qt Quick Controls概述21.1.1 第一个Qt Quick Controls程序21.1.2 Qt Quick窗体应用程序的构成 21.2 Qt Quick控件21.2.1 概述21.2.2 基本控件21.2.3 高级控件21.2.4 样式定制 2…

光电开关-NPN-PNP

基础概念 有信号 “检测到物体/有物体遮挡” 工作原理 NPN&#xff1a;表示共正电压&#xff0c;输出负电压【只能输出低电压或者悬空 常开常闭是指 输出有没有跟“地”接通】&#xff1b; NPN NO&#xff1a;表示常态下是常开的&#xff0c;检测到物体时黑色线输出一个负电压…

docker jira 一键安装含PJ(docker 一键安装jira)

docker jira 一键安装含PJ&#xff08;docker 一键安装jira&#xff09; 本文仅供参考学习&#xff0c;请勿用于商业用途本文用于Jira在Docker的安装&#xff0c;仅用于记录安装方式转载请注明来源Linux安装可参考链接Windows安装可查考链接Docker一键安装Confluence PJ条件允…

配置HBase和zookeeper

一、上传文件 二、解压 tar -zxf ./zookeeper-3.4.5-cdh5.14.2.tar.gz -C /opt/soft/ tar -zxf ./hbase-2.3.5-bin.tar.gz -C ../soft/ 三、改名字 mv ./zookeeper-3.4.5-cdh5.14.2/ zk345 mv ./hbase-2.3.5/ hbase235 四、配置映射 vim /etc/profile#ZK export ZOOKEEPE…

pytorch学习------实现手写数字识别

目录 目标一、思路和流程分析二、准备训练集和测试集2.1、图形数据处理方法2.1.1、torchvision.transforms.ToTensor2.1.2、torchvision.transforms.Normalize(mean, std)2.1.3、torchvision.transforms.Compose(transforms) 2.2、准备MNIST数据集的Dataset和DataLoader三、构建…