【多线程】阻塞队列 | put()方法 | take()方法 | 生产者-消费者模式 |实现阻塞队列

news2024/11/26 7:30:57

文章目录

    • 阻塞队列
      • 1.生产者-消费者模式
        • 生产者消费者模型的意义:
          • 1.解耦合
          • 2.削峰填谷:
      • 2.阻塞队列的使用
            • BlockingQueue
      • 3.实现阻塞队列
          • 唤醒:
          • 使用阻塞队列实现生产者消费者模型


阻塞队列

阻塞队列是一种特殊的队列:

  • 1.是线程安全的。

  • 2.带有阻塞特性

    如果队列为空,继续出队列,就会发生阻塞。直到其他线程往队列中添加队列为止

    如果队列为满,继续入队列, 也会发生阻塞,直到其他线程从队列中取走元素为止

阻塞队列可以来实现生产者-消费者模型。

1.生产者-消费者模式

生产者:把生产出来的内容,放到阻塞队列中。

消费者:从阻塞队列中获取内容。

生产者消费者模型的意义:
1.解耦合

两个模块联系越紧密,耦合就越高。对于分布式系统来说,更加有意义。

在这里插入图片描述

可以使用生产者-消费者模型,实现解耦合的效果。

在这里插入图片描述

2.削峰填谷:

峰:短时间内,请求量比较多时。

谷:请求量比较少时。

在这里插入图片描述

​ 在这种情况下:高峰时段,一旦客户端发起的请求量非常多时,每个A收到的请求,都会立即发给B。此时,A和B的访问量是相同的。但是在实际上,由于不同的服务器,上面跑的业务不同。虽然访问量一样,单个访问,消耗的硬件资源是不一样的。可能服务器A可以承担这些并发量,但是服务器B承担不了,就会挂掉。

在引入了生产者-消费者模型之后,就会解决这类问题。

在这里插入图片描述

  • 当服务器A收到了大量请求之后,A会把对应的请求写入到队列中。B仍然按照之前的节奏来处理请求。(削峰)
  • 一般情况下,峰值不会持续存在,峰值过后,A的请求量就会恢复正常、甚至减低。服务器B就可以在此时,逐渐把积压的请求给处理掉。(填谷)。

2.阻塞队列的使用

BlockingQueue

在这里插入图片描述

  • BlockingQueue是一个具体的接口,所以需要new一个具体的实现。

  • 同时BlockingQueue继承自Queue。也可以使用Queue的方法(没有阻塞属性)

    1.基于数组实现

    2.基于链表实现

  • BlockingQueue带有阻塞的方法:

    ​ put:阻塞式入队列

    ​ take:阻塞式出队列

    没有提供阻塞式获取队首元素的方法。

    public static void main(String[] args) {
       // BlockingQueue<String> queue = new ArrayBlockingQueue<>();
        BlockingQueue<String>queue = new LinkedBlockingQueue<>();
   
        queue.put("111");
        queue.put("222");
        queue.put("333");
        queue.put("444");
        String elem = queue.take();
        System.out.println(elem);
        elem = queue.take();
        System.out.println(elem);
        elem = queue.take();
        System.out.println(elem);
        elem = queue.take();
        System.out.println(elem);

    }

3.实现阻塞队列

给一个普通的队列加上线性安全和阻塞

对入队和出队的方法进行加锁。对数据的修改实现原子性操作,保证线程安全

  • put入队的时候,如果队列满了,就进行阻塞(wait)

在出队的时候,当size–后,队列中有位置了,调用notify()方法,对阻塞的put方法进行唤醒。

  • 同样的,如果take出队列时,队列为空的话,也需要进行阻塞。

在入队时,当size++后,队列不为空了,调用notify()方法,对阻塞的take方法进行唤醒。

在这里插入图片描述

  • 一个队列的阻塞情况,要么为空、要么为满。

    put和take只有一边能阻塞。如果put阻塞了,其他线程继续调用put,也会进行阻塞。只有靠take来唤醒。

    take阻塞,其他线程继续调用take也会进行阻塞,只能靠put来唤醒。

唤醒:

wait方法除了使用notify()方法进行唤醒,还可以通过interrupt()方法,来中断wait的状态。

使用interrupt方法唤醒的时候,会出现InterruptedException异常

public void put(String elem) throws InterruptedException {
    
}

因为是throws抛出的异常,执行到interrupt()方法后,整个方法就会结束。

    public void put(String elem) {
        synchronized (this) {
            if (size == data.length) {
                try {
                    this.wait();
                }catch (InterruptedException e){ 
                }
            }
            data[tail] = elem;
            tail++;
            if (tail == data.length) {
                tail = 0;
            }
            size++;
            this.notify();
        }
  • 如果是try-catch来处理异常。如果出现异常,程序仍会继续执行下去。在满队列的情况下。强行修改,会覆盖掉tail的值,并且size会超出数组长度。

​ 使用wait时,要考虑wait是notify唤醒的,还是通过Interrupt唤醒的。在wait返回时,还要进行判断wait执行的条件符不符合。可以直接将wait写在while循环中。循环的条件就是wait执行的条件。使wait在唤醒之后,再确定一下,条件是否满足。

            while (size == data.length) {
                //队列满了,就会进行堵塞
               this.wait();
            }
  • 最终再通过volatile修饰要频繁修改的变量,避免出现内存可见性问题。
class MyBlockingQueue {
    private String[] data = new String[1000];
    private  volatile int head = 0;
    //队列起始位置
    private volatile int tail = 0;
    //队列结束位置的下一个元素。
    private volatile int size = 0;
    //队列中有效元素个数

    //入队
    public void put(String elem) throws InterruptedException {

        synchronized (this) {
            while (size == data.length) {
                //队列满了,就会进行堵塞
               this.wait();
            }
            //队列没满,向队列添加元素
            data[tail] = elem;
            tail++;
            if (tail == data.length) {
                //满了之后,环形队列要回到开头。
                tail = 0;
            }
            size++;
            this.notify();
            //唤醒take中的wait
        }


    }

    //出队
    public String take() throws InterruptedException {
        synchronized (this) {
            while (size == 0) {
                //队列为空时
               this.wait();
            }
            //队列不空时,把队首head位置删除
            String ret = data[head];
            head++;
            if (head == data.length) {
                head = 0;
            }
            size--;
            this.notify();
            //唤醒put中的wait.
            return ret;
        }
    }
}

使用阻塞队列实现生产者消费者模型
    public static void main(String[] args) {
        MyBlockingQueue queue = new MyBlockingQueue();
        //消费者
        Thread t1 = new Thread(() -> {
            while (true){
                try {
                   String res =  queue.take();
                    System.out.println("消费元素: "+res);
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }

        });
        //生产者
        Thread t2 = new Thread(() -> {
            int num = 1;
            while (true){
                try {
                    queue.put(num+" ");
                    System.out.println("生产元素:"+num);
                    num++;

                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        t1.start();
        t2.start();

    }

生产元素:1001
生产元素:1002
消费元素: 2 
消费元素: 3 
生产元素:1003
消费元素: 4 
生产元素:1004
  • 生产者快速生产了1000多个,消费者才消耗几个。队列填满之后,生产者进入了阻塞。直到消费者消费了之后,才会进行生产。消费一个生产一个。

点击移步博客主页,欢迎光临~

偷cyk的图

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

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

相关文章

【光伏企业】光伏项目怎么做才能提高效率?

一、精细化项目管理 项目规划&#xff1a;在项目启动前&#xff0c;进行充分的调研和规划&#xff0c;明确项目的目标、规模、预算和时间表&#xff0c;确保各项资源得到合理分配。 团队建设&#xff1a;组建一支高效、专业的项目团队&#xff0c;确保团队成员具备光伏领域的…

day02|最小花费爬梯子

最小花费爬梯子 比如 有一个数组 【2 5 20】我们直接选择从1号梯子&#xff08;从零编号&#xff09;跳两格就出去了。 算法原理 我们可以得出楼顶其实是数组的最后一个元素的下一个位置。对于最值问题我们可以尝试使用dpdp我们首先应该定义状态方差的含义&#xff0c;一般以…

亚信安慧AntDB:为安全加码

亚信安慧AntDB分布式数据库凭借平滑扩展、高可用性和低成本三大核心优势&#xff0c;在业界获得了极高的评价和认可。这些优点不仅为AntDB提供了巨大的市场发展潜力&#xff0c;也使其成为众多企业在数据管理上的首选解决方案。 AntDB的平滑扩展特性极大地提升了企业的灵活性和…

官宣:2024第二十届国际铸造件展12月精彩呈现!

Shanghai International Die-casting Casting Expo 2024第二十届上海国际压铸、铸造展览会 2024第二十届上海国际压铸、铸件产品展 时间&#xff1a;2024年12月18-20日 地点&#xff1a;上海新国际博览中心&#xff08;浦东区龙阳路2345号&#xff09; 报名参展&#xff1…

Slf4j+Log4j简单使用

Slf4jLog4j简单使用 文章目录 Slf4jLog4j简单使用一、引入依赖二、配置 log4j2.xml2.1 配置结构2.2 配置文件 三、使用四、使用MDC完成日志ID4.1 程序入口处4.2 配置文件配置打印4.3 多线程日志ID传递配置 五. 官网 一、引入依赖 <dependencies><dependency><g…

STM32之HAL开发——CubeMX配置串行Flash文件系统

配置流程 在开始配置FATFS前&#xff0c;需要提前配置好RCC的时钟&#xff0c;以及时钟的频率&#xff0c;另外还要配置好Debug选项&#xff08;选择串行&#xff09; 选项介绍 文件系统适用于SD卡&#xff0c;Disk磁盘等&#xff0c;需要我们将对应的驱动打开才可以使用。 …

C语言—字符指针,指针数组和数组指针

字符指针 在指针的类型中我们知道有一种指针类型为字符指针 char* ; int main() {char ch w;char *pc &ch;*pc w;return 0; }还有一种使用方式如下&#xff1a; int main() {const char* pstr "hello world.";//这里是把一个字符串放到pstr指针变量里了吗…

雨云免费云服务器领取步骤详解

随着云计算技术的日益普及&#xff0c;越来越多的用户开始选择使用云服务器来满足他们的数据存储和计算需求。雨云作为一家具有自主知识产权的国产云计算服务提供商&#xff0c;其免费云服务器服务备受关注。接下来&#xff0c;本文将为大家详细介绍雨云免费云服务器的领取步骤…

代码随想录-算法训练营day14【二叉树01:理论基础、递归遍历、迭代遍历、统一迭代】

代码随想录-035期-算法训练营【博客笔记汇总表】-CSDN博客 第六章 二叉树part01今日内容&#xff1a; ● 理论基础 ● 递归遍历 ● 迭代遍历 ● 统一迭代详细布置 理论基础 需要了解 二叉树的种类&#xff0c;存储方式&#xff0c;遍历方式 以及二叉树的定义 文章讲解&#x…

2024 NTFS读写工具Tuxera NTFS for Mac 是如何进行下载、安装、激活的

本篇将为各位小伙伴们集中讲解一下NTFS读写工具Tuxera NTFS for Mac 是如何进行下载、安装、激活与换机的。 在数字化时代&#xff0c;数据交换和共享变得日益重要。然而&#xff0c;对于Mac用户来说&#xff0c;与Windows系统之间的文件交换可能会遇到一些挑战。这是因为Mac …

Blender生成COLMAP数据集

最近在做3DGS方向&#xff0c;整理了一下Blender生成自己的数据集。 1 Introduction 在Blender中构建场景&#xff08;light, object, camera&#xff09;&#xff0c;利用Blender的python脚本对其渲染&#xff0c;导出多视角下渲染出的RGB图和depth map&#xff0c;并将trans…

【亲测有用】2024年timm从本地加载模型

有图有真相&#xff1a;https://github.com/huggingface/pytorch-image-models/discussions/1826 好使。 其他的过时的方法就别看了。&#xff08;当然我这个过几年也会过时&#xff09;

亚马逊国际获得AMAZON商品详情 API (属性主图价格等)

要获取亚马逊国际的商品详情API&#xff0c;包括商品的属性、主图、价格等信息&#xff0c;可以按照以下步骤操作&#xff1a; 请求示例&#xff0c;API接口接入Anzexi58 了解亚马逊开发者中心&#xff1a;首先&#xff0c;访问亚马逊开发者中心&#xff0c;详细阅读相关的API…

基于小程序实现的4s店管理系统

作者主页&#xff1a;Java码库 主营内容&#xff1a;SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app等设计与开发。 收藏点赞不迷路 关注作者有好处 文末获取源码 技术选型 【后端】&#xff1a;Java 【框架】&#xff1a;ssm 【…

OpenAI宣布GPT-4-Turbo全面升级,GPT-4 Turbo 新增视觉理解能力,可同时处理文本和图像信息

OpenAI宣布GPT-4-Turbo全面升级&#xff0c;GPT-4 Turbo with Vision新增视觉理解能力&#xff0c;可同时处理文本和图像信息&#xff0c;极大简化了开发流程。 OpenAI宣布GPT-4 Turbo全面升级&#xff01;根据官方说法&#xff0c;这一波 GPT 的升级包括&#xff1a; 更长的上…

如果补货机器人也开始搬砖

过去几年&#xff0c;如果一位顾客的欲购商品疑似缺货&#xff0c;“请稍等&#xff0c;我去后台查一下”便是最常听到的一句话。随着过去几年劳动力短缺的加剧&#xff0c;货架并不总像许多人习惯的那样满满当当。为减轻不断补货的负担&#xff0c;使员工可以将精力集中于其他…

ArduPilot开源飞控之ROS系统简介

ArduPilot开源飞控之ROS系统简介 1. 源由2. ROS系统3. 安装2.1 安装Docker2.2 安装ROS2 4. 总结5. 补充资料 1. 源由 之前在ArduPilot开源飞控之硬件SBC分析中讨论过&#xff0c;个人角度最推荐其中两个系统是&#xff1a; Rpanion-server【推荐&#xff0c;简单】BlueOS【推…

OpenHarmony实战开发-如何实现发布图片评论功能。

介绍 本示例将通过发布图片评论场景&#xff0c;介绍如何使用startAbilityForResult接口拉起相机拍照&#xff0c;并获取相机返回的数据。 效果图预览 使用说明 通过startAbilityForResult接口拉起相机&#xff0c;拍照后获取图片地址。 实现思路 1.创建CommentData类&…

反转链表【java】

给定一个链表的头节点head反转链表 方法一&#xff1a;循环 1.定义三个指针&#xff1a; pre指针&#xff1a;刚开始指向空 prenull cur指针&#xff1a;刚开始指向head节点 curhead temp指针&#xff1a;保存cur指针指向节点的下一个节点 2. 不断循环改变相邻两个节点的指…

3.2 iHRM人力资源 - 组织架构 - 编辑及删除

iHRM人力资源 - 组织架构 文章目录 iHRM人力资源 - 组织架构一、编辑功能1.1 表单弹层并数据回显1.2 编辑校验1.3 编辑 二、删除功能 一、编辑功能 编辑功能和新增功能用的组件其实是一个&#xff0c;结构几乎是一样的&#xff0c;其实是复用了组件&#xff0c;我们也省去了很…