【JavaEE】阻塞队列 + 生产者消费者模型

news2024/11/15 21:58:48

目录

阻塞队列

阻塞队列的使用

生产者消费者模型

模型的两个好处

1. 降低耦合

2. 削峰填谷

简单实现阻塞队列


阻塞队列

阻塞队列是在一般的队列上升级而来的。

对于队列为时,如果还想队列中的元素,此时阻塞队列就会进行阻塞

对于队列为时,如果还想往队列中元素,此时阻塞队列就会进行阻塞

阻塞队列的使用

阻塞队列的具体使用由这个接口 BlockingQueue<>实现。

可以看到,该接口继承了队列,所以它有队列的所有方法。不过在使用阻塞队列时,我们一般只使用以下两种方法

方法说明
void put(value)该方法是往队列里面添加元素,满了就阻塞
Object take()该方法是取出队列的元素,空了就阻塞

其他的方法没有阻塞功能。 

当我们实例化的时候可以看到,有以下三种实现方式。

 简单使用一下阻塞队列。

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class ThreadDemo21 {

    public static void main(String[] args) throws InterruptedException {

        // 这个队列的空间大小设置为 3
        BlockingQueue<Integer> blockingQueue = new ArrayBlockingQueue<>(3);
        blockingQueue.put(1);
        blockingQueue.put(2);
        blockingQueue.put(3);
        // 因为只有一个主线程
        // 当往队列里添加第四个元素时, 这是队列就会进行阻塞
        blockingQueue.put(4);
        
        // 以下代码执行不到了 
        int ret = blockingQueue.take();
        System.out.println(ret);
        ret = blockingQueue.take();
        System.out.println(ret);
        ret = blockingQueue.take();
        System.out.println(ret);
        ret = blockingQueue.take();
        System.out.println(ret);
    }
}

  


生产者消费者模型

对于上图的模型,就是生产者消费者模型。 

阻塞队列通常被应用到生产者消费者模型当中。阻塞队列充当缓冲区。

// 使用阻塞队列实现生产者消费者模型

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class ThreadDemo22 {

    public static void main(String[] args) {
        // 生产者产生的数字都放到阻塞队列中
        // 消费者从阻塞队列中拿到数字
        BlockingQueue<Integer> blockingQueue = new ArrayBlockingQueue<>(1000);

        // t1线程是生产者
        Thread t1 = new Thread(() -> {
            int i = 1;
            while (true) {
                try {
                    blockingQueue.put(i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("生产者: " + i);
                i++;

                // 生产者每隔100ms产生一个数字
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        // t2线程是消费者
        Thread t2 = new Thread(() -> {
            while (true) {
                try {
                    int ret = blockingQueue.take();
                    System.out.println("消费者: " + ret);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        t1.start();
        t2.start();

    }

}

 


 

模型的两个好处

1. 降低耦合

为引入该模型前,耦合度高。

引入该模型后,耦合性降低。

 

2. 削峰填谷

前段时间鹅鸭杀游戏突然爆火,从日均几万人到四五十万人同时在线。此时大量用户数据就会到这个缓冲区当中,不至于一下子全部发送到服务器当中,导致服务器崩溃。这里的缓冲区就起到了削峰的作用;当用户数量下去之后,服务器就从缓冲区中继续读取数据,不至于在这个时间闲着没事干。


简单实现阻塞队列

具体代码的解释都卸载注释中了。

// 实现一个阻塞队列
// 不带泛型, 直接使用Integer类型
// 只实现 带有阻塞功能的 put 和 take 方法

class MyBlockQueue {

    private Integer[] arr = new Integer[100];
    private int head = 0;// 指向对头
    private int tail = 0;// 指向队尾
    private int size = 0;// 计数

    public void put(int value) throws InterruptedException {
        // 加锁只是针对当前的对象
        // 如果实例化了其他对象, 并不会相互影响
        synchronized(this) {
            // 这里while相当于多次if
            // 目的是为了判断唤醒之后是否数组是否还是满的, 满了继续阻塞
            // 因为在多线程下, 有可能顺序会发生变化
            while (size == arr.length) {
                this.wait();
            }
            // 没有满, 在tail指向的位置添加元素, 添加完之后tail后移
            arr[tail] = value;
            tail++;
            // tail如果到了末尾, 它就要从头开始
            if (tail >= arr.length) {
                tail = 0;
            }
            //计数器++
            size++;
            // 此时数组中有元素了, 可以唤醒在take方法中因为数组为空而阻塞的原因了
            this.notify();
        }
    }

    // 由于涉及到多线程, 所以要考虑线程安全问题
    // head tail size都是会改变的变量, 所以考虑synchronized来保持其原子性
    public Integer take() throws InterruptedException {

        synchronized(this) {
            // 如果数组为空, 就要阻塞等待
            // 使用while的理由同上
            while (size == 0) {
                this.wait();
            }
            // 有元素, 开始从head指向的弹出
            Integer ret = arr[head];
            head++;
            if (head >= arr.length) {
                head = 0;
            }
            size--;
            // 此时数组中有空的位置了, 可以唤醒在put方法中因为数组满而阻塞的原因了
            this.notify();
            return ret;
        }
    }
}

public class ThreadDemo24 {

    public static void main(String[] args) {
        // 使用生产者消费者模型测试一下实现的阻塞队列
        // t1 线程为生产者
        // t2 线程为消费者

        MyBlockQueue myBlockQueue = new MyBlockQueue();

        Thread t1 = new Thread(()-> {
            int i = 1;
            while (true) {
                try {
                    myBlockQueue.put(i);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("生产者" + i);
                i++;
                // 让生产者每隔 100ms 生产出一个数字
                // 如果消费者没有时间限制, 这样就会出现队列为空而阻塞的情况
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        Thread t2 = new Thread(()-> {
            while (true) {
                try {
                    int ret = myBlockQueue.take();
                    System.out.println("消费者" + ret);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        t1.start();
        t2.start();
    }
}


有什么错误评论区指出。希望可以帮到你。 

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

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

相关文章

fpga的SD卡读BMP图片显示实验(SPI模式)

对于 SD 卡的 SPI 模式而言&#xff0c;采用的 SPI 的通信模式为模式 3&#xff0c;即 CPOL1&#xff0c;CPHA1&#xff0c;在 SD 卡 2.0 版本协议中&#xff0c;SPI_CLK 时钟频率可达 50Mhz。SD 卡的 SPI 模式&#xff0c;只用到了 SDIO_D3&#xff08;SPI_CS&#xff09;、SD…

Nacos 部署简单使用

文章目录1、前置相关知识及说明2、官网3、环境4、Nacos 和 Zookeeper、Eureka 的主要区别5、安装部署 & 启动5.1、Windows下载安装包部署单机部署集群部署测试6、使用服务端客户端 - SpringBoot 使用 Nacos Client7、运维健康检查获取配置&#xff0c;验证服务端是否正常异…

【DX-BT24蓝牙模块连接Arduino与手机透传教程】

【DX-BT24蓝牙模块连接Arduino与手机透传教程】1. 前言2. 接线3. 程序设计详解4. 演示效果5. 小结1. 前言 大夏龙雀科技DX-BT24&BT24-S&BT24-PA蓝牙模块,拥有5.1蓝牙协议,模块内置标准串口协议。前期设置蓝牙名称为VOR&#xff0c;采用默认波特率9600&#xff0c;详细…

JavaScript 入门基础 - 对象(五)

JavaScript 入门基础 - 对象 文章目录JavaScript 入门基础 - 对象1. 对象1.1 对象的基本理解1.2 为什么需要变量2. 创建对象的方式2.1 利用字面量创建对象2.2 变量属性函数方法的区别2.3 利用 new Object 创建对象2.4 利用构造函数创建对象3.new关键字4. 遍历对象属性5. JavaSc…

二、Promise

Promise1、回调地狱1.1 如何解决回调地狱的问题1.2 Promise 的基本概念2、基于回调函数按顺序读取文件内容3. 基于 then-fs 读取文件内容3.1 then-fs 的基本使用3.2 .then() 方法的特性3.3 基于 Promise 按顺序读取文件的内容3.4 通过 .catch 捕获错误3.4 通过 .catch 捕获错误…

基于RBAC权限控制模型的管理系统的设计与实现

文章目录一、RBAC 权限设计1.1 模型概述1.2 模型分类二、基于RBAC的后台管理系统2.1 项目概述2.2 技术选型2.3 内部处理流程2.4 功能模块展示2.5 权限控制展示2.6 下载说明一、RBAC 权限设计 1.1 模型概述 基于角色的访问控制 RBAC&#xff0c;是实施面向企业安全策略的一种有…

kafka的介绍和基本使用

文章目录Kafka介绍1.Kafka的使用场景2.Kafka基本概念kafka基本使用1.安装前的环境准备2.启动kafka服务器3.创建主题topic4.发送消息5.消费消息Kafka介绍 Kafka是最初由Linkedin公司开发&#xff0c;是一个分布式、支持分区的&#xff08;partition&#xff09;、多副本的 &…

载波相位误差对BPSK解调性能的影响理论推导

在上一篇博客基础上,继续讨论载波相位误差对解调性能的影响! 【通信原理】通信原理书中解调器输入端信噪比a2/(2σ2)与比特信噪比Eb/No有什么关系? 以BPSK为例,从解调原理图可以看出,当本地参考载波信号与原始载波存在相位误差时,假设相位差为 φ \varphi φ,则解调器低…

【Linux】了解磁盘/文件系统/inode

文章目录一.磁盘1.磁盘的结构2.磁盘的定位&#xff08;寻找方案&#xff09;3.磁盘的分区与格式化介绍二.理解inode三.ext2文件系统的存储方案一.磁盘 1.磁盘的结构 问题1&#xff1a;什么是磁盘&#xff1f; 磁盘是在冯诺依曼体系结构中几乎唯一的机械设备&#xff0c;机械设…

AntDB数据库助力中国移动结算中心建设

为响应中国移动集团公司IT集中化的要求&#xff1a;全面落实“十三五”十大战略工程&#xff0c;加快“推动公司IT资源一体化整合“重点专项工作。以IT系统为载体&#xff0c;构建高效运营支撑体系&#xff0c;形成集中化支撑和协同业务支撑模式&#xff0c;打造极致体验、高效…

列表初始化(内置类型、自定义类型)

列表初始化的特性来源于单参数的隐式类型转换。以下面这个赋值为例&#xff0c;我们可以理解成 先创建一个匿名对象Point(2)&#xff0c;这个时候就变成了 Point p Point(2);然后会调用拷贝构造。 虽然隐式转换的可以这样理解&#xff0c;但是最后会被编译器优化成直接调用有…

[Android]Bitmap Drawable

在实际开发中&#xff0c;我们可以直接引用原始的图片&#xff0c;但是也可以通过xml的方式来描述它&#xff0c;通过xml来描述的BitmapDrawable可以设置更多效果。 <?xml version"1.0" encoding"utf-8"?> <bitmap xmlns:android"http://…

java spring IOC Bean管理操作讲解 并代码演示xml的实现方式

查看本文 需要您使用spring创建过对象管理 如果之前没有接触过 可以先查看我的文章 java 手把手带你创建一个spring入门案例 IOC 操作中 Bean管理主要有两个部分 分别是创建对象和注入属性 他们都有两种实现方式 分别是xml和注解方式实现 本文只演示xml 后续我会出注解方式的文…

第十三届蓝桥杯省赛 JAVA A组 - 蜂巢

✍个人博客&#xff1a;https://blog.csdn.net/Newin2020?spm1011.2415.3001.5343 &#x1f4da;专栏地址&#xff1a;蓝桥杯题解集合 &#x1f4dd;原题地址&#xff1a;蜂巢 &#x1f4e3;专栏定位&#xff1a;为想参加蓝桥别的小伙伴整理常考算法题解&#xff0c;祝大家都能…

背包问题——“0-1背包”,“完全背包”(这样讲,还能不会?)

目录 一、0-1背包 1.1、0-1背包解决的问题 1.2、dp数组定义 1.3、转移方程 1.3.1、二维dp数组 1.3.2、一维dp数组 1.4、遍历顺序 1.5、测试代码 1.6、练习 二、完全背包 2.1、完全背包解决问题 2.2、与0-1背包的区别 2.3、测试代码 2.4、拓展问题&#xff1a;装满…

【2022】13 年终总结

新年Flag 2023年&#xff0c;为了各方面能有所进步&#xff0c;列一些希望达成的目标和想做的事&#xff0c;到年底看看效果。 撰写一篇英文论文 申请到CSC 和xl去外地玩两次 想到了再加 去年Flag倒了几个&#xff1f; 一维河网水动力学模型导师说不用自己编&#xff0c;看懂…

Numpy文件交互:.npy和.npz有什么区别?

文章目录saveloadsavezsavez_compressedNumpy提供了以.npy为后缀的文件存储方案&#xff0c;与这种文件格式密切相关的读、写函数分别是np.load和np.save。通过savez可以一次性存储多个数组&#xff0c;并可通过load以键值对的形式读取出来&#xff1b;如果觉得文件太大&#x…

Mybatis缓存

内存中的一块存储空间&#xff0c;服务于某个应用程序&#xff0c;旨在将频繁读取的数据临时保存在内存中&#xff0c;便于二次快速访问。 一级缓存 SqlSession级别的缓存&#xff0c;同一个SqlSession的发起多次同构查询&#xff0c;会将数据保存在一级缓存中。 注意&#x…

【NI Multisim 14.0虚拟仪器设计——放置虚拟仪器仪表(频率特性测试仪)】

目录 序言 &#x1f34d;放置虚拟仪器仪表 &#x1f349;频率特性测试仪 &#x1f34a;&#x1f34a;1.“模式”选项组 &#x1f34a;&#x1f34a;2.“水平”选项组 &#x1f34a;&#x1f34a;3.“垂直”选项组 &#x1f34a;&#x1f34a;4.“控件”选项组 序言 N…

SpringBoot+Vue项目大学生租房平台

文末获取源码 开发语言&#xff1a;Java 框架&#xff1a;springboot JDK版本&#xff1a;JDK1.8 服务器&#xff1a;tomcat7 数据库&#xff1a;mysql 5.7/8.0 数据库工具&#xff1a;Navicat11 开发软件&#xff1a;eclipse/myeclipse/idea Maven包&#xff1a;Maven3.3.9 浏…