多线程案例---阻塞队列

1. 阻塞队列

阻塞队列是一种特殊的队列,也遵守 " 先进先出 " 的原则。

阻塞队列是一种线程安全的数据结构,并且具有以下特性:

1. 当队列为满时,继续进行入队列操作就会阻塞,直到有其他线程从队列中取走元素

2. 当队列为空时,继续进行出队列操作就会阻塞,直到有其他线程往队列中插入元素

阻塞队列的一个典型应用场景就是”生产者消费者模型“ 。

1.1 生产者消费者模型 

在多线程编程中,生产者消费者模型是一种典型的编码技巧,生产者消费者模型通过阻塞队列来解决生产者和消费者强耦合的问题。

在生产者消费者模型中,生产者不与消费者直接进行通讯,而是通过阻塞队列来间接的进行通讯。当生产者产出一个数据后,它不会等待消费者来处理,而是将产出的数据扔到阻塞队列中,等到消费者去处理数据时,消费者也不会直接跟生产者去要数据,而是从阻塞队列中获取需要处理的数据。

如下图

在生产者消费者模型中引入阻塞队列有什么影响呢?

1 .解耦合

      我们假设有两个服务器,分别为A服务器和B服务器,当A服务器和B服务器直接进行交流时,编写A服务器的代码时,多多少少也会涉及到一点B服务器的逻辑,编写B服务器的代码时多多少少也会涉及到一些A服务器的逻辑,这样就导致了两个服务器的代码就有了强耦合性。而当我们引入阻塞队列之后,就会变成A服务器与阻塞队列进行交涉,B服务器和阻塞队列进行交涉,从而间接完成A服务器和B服务器的间接交涉,这样A服务器和B服务器就不会直接进行交涉了。此时,A服务器中的代码中就看不到B服务器了,B服务器中的代码中就看不到A服务器了。这样就降低了代码大的耦合性。

2.削峰填谷

    服务器接收的数据量可以理解为一个波形图,当A服务器短时间内接收的数据量达到一个峰值时,很容易将A服务器搞挂,此时如果将这些大量的数据直接交给服务器来处理,B服务器也很可能挂掉。但是,如果有了一个阻塞队列,当数据的发送达到一个峰值时,A服务器可以将数据放到阻塞队列中,防止A服务器挂掉。当数据大的发送量达到一个波谷时,B服务器就可以利用数据的传送处于波谷的时间去处理阻塞队列中的数据,从而防止B服务器因为一股脑处理大量数据而挂掉。

1.2 标准库中的阻塞队列 

 在Java标准库中内置了一个阻塞队列,如果我们需要在一些程序中使用阻塞队列,使用标准库提供的即可

1. Java标准库提供的标准库是一个名为BlockingQueue的接口,真正实现的类是LinkedBlockingQueue 

2. put方法用于阻塞式的入队列,take方法用于阻塞式的出队列

3. BlockingQueue也有offer,poll,peek等方法,但是这些方法没有阻塞性

1.3 用标准库的阻塞队列实现一个生产者消费者模型

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

public class Demo26 {
    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<Integer> blockingQueue=new LinkedBlockingQueue<>(100);

        Thread producer=new Thread(()->{
            try{
                int id=0;
                for(int i=0;i<100;i++){
                    Thread.sleep(1000);
                    blockingQueue.put(id);
                    System.out.println("producer生产数据:"+id);
                    id++;
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }

        },"producer");

        Thread consumer=new Thread(()->{
            try{
                for (int i = 0; i < 100; i++) {
                    Thread.sleep(1000);
                    int take=blockingQueue.take();
                    System.out.println("consumer消费数据:"+take);
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        },"consumer");
        producer.start();
        consumer.start();
        producer.join();
        consumer.join();
    }
}

 

2.模拟实现一个阻塞队列

实现思路:

1.由于阻塞队列要不断有数据入队列和出队列,我们呢可以使用循环队列来实现

2.组要模拟实现put方法和take方法

3.在put过程中,发现队列满了或者在take过程中,发现队列为空,我们就要wait

4.注意wait要搭配一个循环来使用,因为wait也有可能被Interrupt这样的方法唤醒

5.使用syncronized进行加锁控制

代码实现:

class MySubBLockingQueue{
    private volatile int[] data=null;
    private volatile int head=0;
    private volatile int tail=0;
    private volatile int size=0;

    public MySubBLockingQueue(int capacity){
        data=new int[capacity];
    }

    public void put(int number) throws InterruptedException {
        synchronized (this){
            //判断队列是否满了
            while (size==data.length){
                this.wait();
            }
            data[tail]=number;
            tail++;
            size++;
            if(tail>=data.length){
                tail=0;
            }
            this.notify();
            //tail=(tail+1)%data.length;
        }
    }

    public int take() throws InterruptedException {
        int back=0;
        synchronized (this){
            //判断队列是否为空
            while (size==0){
                this.wait();
            }
            back=data[head];
            head++;
            size--;
            if(head>= data.length){
                head=0;
            }
            //head=(head+1)%data.length;
            this.notify();
        }
        return back;
    }
}

 

3.用模拟实现的阻塞队列实现生产者消费者模型

class MySubBLockingQueue{
    private volatile int[] data=null;
    private volatile int head=0;
    private volatile int tail=0;
    private volatile int size=0;

    public MySubBLockingQueue(int capacity){
        data=new int[capacity];
    }

    public void put(int number) throws InterruptedException {
        synchronized (this){
            //判断队列是否满了
            while (size==data.length){
                this.wait();
            }
            data[tail]=number;
            tail++;
            size++;
            if(tail>=data.length){
                tail=0;
            }
            this.notify();
            //tail=(tail+1)%data.length;
        }
    }

    public int take() throws InterruptedException {
        int back=0;
        synchronized (this){
            //判断队列是否为空
            while (size==0){
                this.wait();
            }
            back=data[head];
            head++;
            size--;
            if(head>= data.length){
                head=0;
            }
            //head=(head+1)%data.length;
            this.notify();
        }
        return back;
    }
}
public class Demo27 {
    public static void main(String[] args) throws InterruptedException {
        MySubBLockingQueue bLockingQueue=new MySubBLockingQueue(100);
        Thread producer=new Thread(()->{
            try{
                int id=0;
                for (int i = 0; i < 100; i++) {
                    Thread.sleep(1000);
                    bLockingQueue.put(id);
                    System.out.println("producer生产数据:"+id);
                    id++;
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }

        },"producer");
        Thread consumer=new Thread(()->{
            try{
                for (int i = 0; i < 100; i++) {
                    Thread.sleep(1000);
                    int take=bLockingQueue.take();
                    System.out.println("consumer消费数据:"+take);
                }
            }catch (InterruptedException e){
                e.printStackTrace();
            }
        },"consumer");
        producer.start();
        consumer.start();
        producer.join();
        consumer.join();
    }
}

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

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

相关文章

【CANOE】【学习】【诊断功能】功能寻址和物理寻址

文章目录 前言一、功能寻址和物理寻址是什么&#xff1f;二、说明三、在脚本Capl里面进行使用 前言 这边文章我们将要学习和理解功能寻址和物理寻址。 一、功能寻址和物理寻址是什么&#xff1f; 可以很简单的一句话去理解&#xff1a; 物理寻址&#xff1a;是每个ECU的物理…

VisionPro —— CogIPOneImgeTool工具详解

CogIPOneImageTool工具主要用来对单张图像进行算法处理操作 CogIPOneImgeTool简介 CogIPOneImageTool 工具可完成高斯平滑、高通滤波和图像量化等基本图像处理操作。Image Processing One Image 工具编辑控件为此工具提供图形用户界面。 Image Processing Operations (图像处…

从分析Vue实例生命周期开始,剖析Vue页面跳转背后执行过程

文章目录 1.概要2.Vue实例生命周期3.生命周期函数解释4.存在父子组件情况页面执行过程5. 分析路由跳转页面执行过程6.扩展补充7.小结 1.概要 本文旨在分析Vue页面进行路由切换时&#xff0c;Vue背后的运行过程&#xff0c;旨在让大家更加清晰地明白Vue页面运行过程中钩子方法的…

43.第二阶段x86游戏实战2-提取游戏里面的lua

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 本人写的内容纯属胡编乱造&#xff0c;全都是合成造假&#xff0c;仅仅只是为了娱乐&#xff0c;请不要…

是时候用开源降低AI落地门槛了

过去三十多年&#xff0c;从Linux到KVM&#xff0c;从OpenStack到Kubernetes&#xff0c;IT领域众多关键技术都来自开源。开源技术不仅大幅降低了IT成本&#xff0c;也降低了企业技术创新的门槛。 那么&#xff0c;在生成式AI时代&#xff0c;开源能够为AI带来什么&#xff1f;…

xlwings,让excel飞起来!

excel已经成为必不可少的数据处理软件&#xff0c;几乎天天在用。python有很多支持操作excel的第三方库&#xff0c;xlwings是其中一个。 关于xlwings xlwings开源免费&#xff0c;能够非常方便的读写Excel文件中的数据&#xff0c;并且能够进行单元格格式的修改。 xlwings还…

【分布式事务】二、NET8分布式事务实践: DotNetCore.CAP 框架 、 消息队列(RabbitMQ)、 数据库(MySql、MongoDB)

介绍 [CAP]是一个用来解决微服务或者分布式系统中分布式事务问题的一个开源项目解决方案, 同样可以用来作为 EventBus 使用 github地址:https://github.com/dotnetcore/CAP官网地址: https://cap.dotnetcore.xyz/官网文档:https://cap.dotnetcore.xyz/userguide/zh/cap/id…

【论文阅读】Learning dynamic alignment via meta-filter for few-shot learning

通过元滤波器学习动态对齐以实现小样本学习 引用&#xff1a;Xu C, Fu Y, Liu C, et al. Learning dynamic alignment via meta-filter for few-shot learning[C]//Proceedings of the IEEE/CVF conference on computer vision and pattern recognition. 2021: 5182-5191. 论文…

IDEA 2024使用mybatisplus插件生成代码在项目中

在IDEA 插件市场搜索“mybatisplus”插件并安装&#xff0c;安装好后重启IDEA&#xff0c;安装过程网上很多教程&#xff0c;这里略过&#xff1b;IDEA 2024配置数据库和生成代码迁移到了Tools菜单下&#xff0c;原先版本在Other; 先完成数据库配置&#xff0c;点击Config Data…

Android CCodec Codec2 (十九)C2LinearBlock

在上一篇文章的结尾&#xff0c;我们看到fetchLinearBlock方法最终创建了一个C2LinearBlock对象。这一节&#xff0c;我们将深入了解C2LinearBlock是什么&#xff0c;它的作用是什么&#xff0c;以及它是如何被创建的。 1、_C2BlockFactory 先对上一篇文章的结尾内容做简单回顾…

LabVIEW离心泵性能优化测试系统

开发了一套基于LabVIEW平台开发的离心泵性能优化测试系统。系统集成了数据采集、流量控制、数据存储、报表生成等功能&#xff0c;提供了低成本、便捷操作的解决方案&#xff0c;适用于工业场景中对离心泵性能的精确测评。 项目背景 随着工业化进程的加速&#xff0c;离心泵在…

【NLP自然语言处理】深入探索Self-Attention:自注意力机制详解

目录 &#x1f354; Self-attention的特点 &#x1f354; Self-attention中的归一化概述 &#x1f354; softmax的梯度变化 3.1 softmax函数的输入分布是如何影响输出的 3.2 softmax函数在反向传播的过程中是如何梯度求导的 3.3 softmax函数出现梯度消失现象的原因 &…

MML 中使用 libevent +std::async unix socket domain 进程间通信

可以执行大量超时的接口,直到任务执行完成 还可以在一个事件做检测&#xff0c;funtcure 中的值为ready 状态 uds 的用法和tcp 类似&#xff0c;会维护一个链接状态和分配一个链接套接字,这就为异步执行提供了很方便的条件 客户端就安静的做一个计时,看是否在固定事件内返回执行…

基础算法练习--滑动窗口(已完结)

算法介绍 滑动窗口算法来自tcp协议的一种特性,它的高效使得其也变成了算法题的一种重要考点.滑动窗口的实现实际上也是通过两个指针前后遍历集合实现,但是因为它有固定的解题格式,我将其单独做成一个篇章. 滑动窗口的解题格式: 首先,定义两个指针left和right,与双指针不同的…

算法:只出现一次的数字II

题目 链接&#xff1a;leetcode链接 思路分析 这道题目其实是一个观察题&#xff0c;比较考察观察能力。 数组中只有一个元素只出现一次&#xff0c;其他的元素都出现三次 我们假设有n个元素出现三次 那么所有的元素的第i位的和加起来只有下面的四种情况 3n * 0 0 3n * 0…

【rust】rust基础代码案例

文章目录 代码篇HelloWorld斐波那契数列计算表达式&#xff08;加减乘除&#xff09;web接口 优化篇target/目录占用一个g&#xff0c;仅仅一个actix的helloWorld demo升级rust版本&#xff0c; 通过rustupcargo换源windows下放弃吧&#xff0c;需要额外安装1g的toolchain并且要…

鸿蒙基本组件结构

组件结构 1. 认识基本的组件结构 ArkTS通过装饰器Component 和Entry 装饰 struct 关键字声明的数据结构&#xff0c;构成一个自定义组件 自定义组件中提供了一个build函数&#xff0c;开发者需要在函数内以链式调用的方式进行基本的UI描述&#xff0c;UI描述的方法请参考UI描述…

Python并发编程库:Asyncio的异步编程实战

Python并发编程库&#xff1a;Asyncio的异步编程实战 在现代应用中&#xff0c;并发和高效的I/O处理是影响系统性能的关键因素之一。Python的asyncio库是专为异步编程设计的模块&#xff0c;提供了一种更加高效、易读的并发编程方式&#xff0c;适用于处理大量的I/O密集型任务…

快速开发工具 Vite

快速开发工具 vite 摘要&#xff1a; **概念&#xff1a;**Vite 是一种新型前端构建工具&#xff0c;能够显著提升前端开发体验 **构造&#xff1a;**Vite 主要由一个开发服务器和一套构建指令组成。 Vite底层的服务器转换和转发&#xff1a;以处理ts文件为例 1-读取 forma…

Servlet-Filter

文章目录 一. Filter 过滤器1. 概括2. 原理3. api配置过滤器(Filter)拦截路径1.xml 方式2.注解方式 4. 生命流程a.执行流程b.拦截路径c.过滤器链 5. 登录校验-Filter 一. Filter 过滤器 1. 概括 过滤器&#xff0c;顾名思义就是对事物进行过滤的&#xff0c;在 Web 中的过滤器…