【JAVAEE】阻塞队列的实现

news2025/1/4 15:24:21

目录

阻塞队列

生产者消费者模型

消息队列

消息队列的作用

1.解耦

2.削峰填谷

3.异步

演示JDK中的阻塞队列

实现一个阻塞队列


阻塞队列

队列,是一种先进先出(FIFO)数据结构。

阻塞队列也满足队列的特性:

队元素时,先判断一下队列是否已经了,如果满了就等待(阻塞),当有空余空间时再插入

队元素时,先判断一下队列是否已经了,如果空了就等待(阻塞),当队列中有元素时再取出

阻塞队列的一个典型应用场景就是 " 生产者消费者模型 ". 这是一种非常典型的开发模型 .

生产者消费者模型

生产者和消费者模型使通过一个容器来解决生产者和消费者的强耦合问题。

生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取。

例1:分别用一个线程模拟生产者和消费者:

生产者:

    //创建生产者线程
        Thread producer=new Thread(()->{
            int num=1;
            while(true){
                //生产(打印)一条消息
                System.out.println("生产了消息"+num);
                try {
                    //把消息放入阻塞队列中
                    queue.put(num);
                    num++;
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

消费者:

    //创建消费者线程
        Thread consumer=new Thread(()->{
            while(true){
                try {
                    //从队列中获取消息
                    int num=queue.take();
                    //打印一下消费日志
                    System.out.println("消费了元素"+num);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });

正常执行线程,生产者生产一个,消费者消费一个:

将生产者休眠时间改为1ms,消费者休眠时间改为1s,模拟生产块消费慢的过程

生产者:

消费者:

生产块消费慢,使用阻塞队列。

例2:现实生活中包饺子的例子

包饺子有三个步骤:

1.和面

2.擀皮

3.包饺子

而这里,生产者就是擀皮的人,消费者使包饺子的人,放饺子皮的地方是一个交易场所,用阻塞队列来是实现。

这就是一个应用场景,用阻塞队列实现饺子皮的交易。

消息队列

消息队列本质上就是一个阻塞队列,在此基础上上为放入阻塞队列的消息打一个标签。

例如:

在基础数据结构上,做了一些针对应用场景的优化和实现,我们把这样的框架和软件,称为“中间件”。

消息队列的作用

1.解耦

在设计程序的时候提出过一些要求,比如:高内聚,低耦合

高内聚:把功能强相关的代码写在一起,维护起来非常方便,是组织代码的方式

低耦合:不要把相关的代码写的到处都是,一般是通过抽象的方式把代码封装成方法,使用的时候调用即可

例如:

购物功能,三个服务器,服务器A负责订单,服务器B负责支付,服务器C查看物流

 现在这个情况,使服务器A与服务器B必须都知道对方存在,且参数必须约定好,如果其中一方损坏那么整个流程都会受到影响。这种情况下,某一个功能需要修改那么涉及的服务器肯都需要重新修改代码。这种情况称之为耦合度非常高。

而通过消息队列把三个系统进行解耦,使它们不再直接通信,起到了单独维护,单独运行的作用。

2.削峰填谷

峰与谷指的是消息的密集程度。

在流量激增的时候用消息队列缓冲,在流量减少的时候,把消息队列中存储的消息一点一点的消费。

例如:

双十一或者微博有热点事件时。

 加入消息队列

在流量激增的时候用消息队列缓冲,在流量减少的时候,把消息队列中存储的信息一点一点消费。

3.异步

同步:请求方必须死等对方的响应

异步:发出请求之后,自己去干别的事情,有响应时会接收到通知从而处理响应

演示JDK中的阻塞队列

创建一个阻塞队列,指定队列容量为3.

//创建时可以指定队列的容量,当队满时还需要插入就要阻塞队列
BlockingDeque<Integer>queue=new LinkedBlockingDeque<>(3);

由于我们创建的阻塞容量为3,当第四个元素插入时就会发生阻塞。

//往队列中写入元素
queue.put(1);
queue.put(2);
queue.put(3);
System.out.println("已经插入了三个元素");
queue.put(4);
System.out.println("已经插入了4个元素");

阻塞队列中获取元素用take,会产生阻塞效果。

public static void main(String[] args) throws InterruptedException {
        //创建时可以指定队列的容量,当队满时还需要插入就要阻塞队列
        BlockingDeque<Integer>queue=new LinkedBlockingDeque<>(3);
        //往队列中写入元素
        queue.put(1);
        queue.put(2);
        queue.put(3);
        System.out.println("已经插入了三个元素");
//        queue.put(4);
//        System.out.println("已经插入了4个元素");
        //阻塞队列中获取元素用take
        System.out.println(queue.take());
        System.out.println(queue.take());
        System.out.println(queue.take());
    }

再次调用queue.take(),此时队列已经空了,进入阻塞状态。

实现一个阻塞队列

1.实现一个普通队列,底层用了两种数据结构,一个是链表,一个是循环数组

2.阻塞队列就是在普通队列上加入了阻塞等待的操作

这里使用数组演示:

普通队列

定义队列的队首,队尾,元素个数

    //定义保存元素的数组
    private int[] elementData=new int[3];
    //定义队首下标
    private int head;
    //定义队尾下标
    private int tail;
    //定义有效元素个数
    private int size;

插入操作

    //插入
    public void put(int value){
        if(size>=elementData.length){
            return;
        }
        elementData[tail]=value;
        tail++;
        if(tail>=elementData.length){
            tail=0;
        }
        size++;
    }

获取值操作

    //获取元素
    public int take(){
        if(size<=0){
            return -1;
        }
        int val=elementData[head];
        head++;
        if(head>=elementData.length){
            head=0;
        }
        size--;
        return val;
    }

阻塞队列

阻塞队列需要在普通队列的基础上加入等待和唤醒操作

等待操作:wait()

唤醒操作:notify()

这两个方法和synchronized是强相关的,所需需要先在代码中加入synchronized。

插入操作

    //插入
    public void put(int value) throws InterruptedException {
        //根据修改共享变量的范围加锁
        //锁对象用this即可
        synchronized (this) {
            if (size >= elementData.length) {
                //如果数组满了,阻塞等待
                this.wait();
            }
            elementData[tail] = value;
            tail++;
            if (tail >= elementData.length) {
                tail = 0;
            }
            size++;
            //唤醒操作
            this.notifyAll();
        }
    }

查找值操作

    //获取元素
    public int take() throws InterruptedException {
        synchronized (this) {
            if (size <= 0) {
                this.wait();
            }
            int val = elementData[head];
            head++;
            if (head >= elementData.length) {
                head = 0;
            }
            size--;
            //唤醒操作
            this.notifyAll();
            return val;
        }
    }

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

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

相关文章

详细版易学版TypeScript - 泛型详解

一、泛型的基本使用 泛型&#xff1a;定义时不确定是什么类型&#xff0c;就先定义为泛型&#xff0c;等到使用时再去确定具体的类型 //实现需求&#xff1a;根据传入的数据value和数量count&#xff0c;返回有count个value值的数组 const myFuncTest1 (value: number, count:…

一文搞懂Bootloader跳转到APP 的方法和原理

一 跳转方法 1、检查栈顶地址是否合法 if (((*(uint32_t*)(NRF52840_APP_BASE)) & 0xffff0000 ) 0x20040000 ){nrf_bootloader_app_start();}在编译生成的APP.bin文件中,前4个字节存放的是__initial_sp&#xff0c;紧接着第二个地址存放的是Reset_Handler&#xff1b;这两…

【机器学习】决策树(基础篇)

决策树&#xff08;基础篇---分类问题&#xff0c;回归问题会另出一篇博客,但也是基础篇&#xff09; 思维导图前言了解决策树&#xff08;前后观看&#xff09;使用决策树&#xff08;感性的认识&#xff09;如何生成决策树&#xff08;原理部分&#xff0c;此部分有局限性&am…

一次失败的面试经历:我只想找个工作,你却用面试题羞辱我

金三银四就要来了&#xff0c;即将又是一波求职月&#xff0c;面对跳槽的高峰期&#xff0c;很多软件测试人员都希望能拿一个满意的高薪offer&#xff0c;但是随着招聘职位的不断增多&#xff0c;面试的难度也随之加大&#xff0c;而面试官更是会择优录取 小王最近为面试已经焦…

redis与mysql事务区别

mysql事务具有原子性&#xff0c;隔离性&#xff0c;一致性的特点。 redis提供multi&#xff0c; exec&#xff0c;watch来支持事务&#xff1a; 原子性&#xff0c;一致性&#xff1a; redis保证在multi&#xff0c;exec之间的语句作为一个整体执行&#xff0c;redis在exec后&…

【Linux进阶之路】基本指令(上)

文章目录 * —— 通配符与ls搭配使用与 rm 搭配使用 ctrl C——终止当前操作man——指令的指南man manman printfman pwd echo ——输出指定内容echo 字符串 cat ——打印文件呢内容cat 文件名常用选项 moreless常用 head——查看文件的前N行内容tail| ——管道cp——拷贝文…

【Android车载系列】第13章 车载渲染-OpenGL实现屏幕渲染

1 OpenGL渲染 上一章节我们通过SurfaceFlinger拿到Surface进行图像绘制&#xff0c;这节课我们通过GLSurfaceView来进行绘制&#xff0c;把摄像头的数据采集后展示渲染在屏幕上&#xff0c;这种方式是在GPU进行处理和绘制。 1.1 渲染使用GLSurfaceView 自定义CarView继承GLS…

考研数学武忠祥 高等数学0基础课笔记 函数和映射

常见的函数 取整函数的基本性质 函数的有界性 例题 sinx 是从-1到1的&#xff0c;但是x是无界的 遇到这种带sin的&#xff0c;就要试着取特殊值&#xff0c;让它为1或者为0 函数的单调性 函数的奇偶性 函数的周期性 举例 数学中Q表示有理数集&#xff0c;下面那个符号表示…

Linux 部署 Nexus (下载、安装、使用)

----仅供学习 如有侵权 联系删除----- 1、下载 下载方式1&#xff1a;官网下载 //建议用迅雷 https://help.sonatype.com/repomanager3/product-information/download/download-archives—repository-manager-3下载方式2&#xff1a;百度云盘下载 文件名&#xff1a;nexus-3…

一个让阿里面试官都说好软件测试简历模板

作为软件测试的垂直领域深耕者&#xff0c;面试或者被面试都是常有的事&#xff0c;可是不管是啥&#xff0c;总和简历有着理不清的关系&#xff0c;面试官要通过简历了解面试者的基本信息、过往经历等&#xff0c;面试者希望通过简历把自己最好的一面体现给面试官&#xff0c;…

数据库表设计规范—三范式、反范式

1.第一范式&#xff1a; 表中的属性不可分割 改为&#xff1a; 2.第二范式&#xff1a; 非主属性必须完全依赖主属性&#xff0c;不能部分依赖&#xff0c;比如只依赖联合主键中的其中一个主键就能拿到数据&#xff0c;这是不符合第二范式的 3.第三范式&#xff1a; 非主…

【Linux】缓存数据库Memcached、Memcached 安装、Memcached应用实例配置

一、 什么是缓存 缓存是指可以进行高速数据交换的存储器&#xff0c;它先于内存与CPU交换数据&#xff0c;因此速率很快。 从性能分析&#xff1a; CPU缓存>内存>磁盘>数据库 从性能来看内存是介于CPU和磁盘&#xff0c;在实际中内存是CPU和磁盘的桥梁。buffer和cache…

一、 JSP01 初识动态网页

一、 JSP01 初识动态网页 1.1 Web 应用程序开发 1.1.1 C/S 架构 客户端&#xff08;Client&#xff09;/服务器&#xff08;Server&#xff09;架构&#xff08;即 C/S 架构&#xff09;的系统主其分为客户端和用户端两层用户需要在本地安装客户端软件&#xff0c;通过网络与…

白话文讲计算机视觉-第十一讲-Harris算子

Moravec算子 说白了就是求两个像素点之间的差&#xff0c;然后平方一下给它变成正值。 其中&#xff0c;x&#xff0c;y表示像素点&#xff0c;u、v表示水平竖直方向的偏移量&#xff1b;w(x,y)为滤波函数&#xff0c;一般直接等于常数1。 I(xu,xv)、I(x,y )表示像素点(xu,xv)、…

基于zemax的折叠光路的激光扩束系统设计

激光扩束系统是激光干涉仪、激光测距仪、激光雷达等诸多仪器设备的重要组成部分&#xff0c;其光学系统多采用通过倒置的望远系统&#xff0c;来实现对激光的扩束&#xff0c;其主要作用是压缩激光束的空间发散角&#xff0c;使扩束后的激光束口径满足其他系统的要求。 激光器…

MySQL-图形化界面工具 (上)

♥️作者&#xff1a;小刘在C站 ♥️个人主页&#xff1a;小刘主页 ♥️每天分享云计算网络运维课堂笔记&#xff0c;努力不一定有收获&#xff0c;但一定会有收获加油&#xff01;一起努力&#xff0c;共赴美好人生&#xff01; ♥️树高千尺&#xff0c;落叶归根人生不易&…

【sorting+双指针+数学】CF845div2 C. Quiz Master

和我一开始想的不太一样 一开始想的也是排序&#xff0c;然后双指针&#xff0c;但是我想的双指针是l1,rn的&#xff0c;因为我没注意到极差尽可能小这个条件可以转化为区间长度最短 其实就是尺取法&#xff0c;然后合法性就是这个区间内的数的所有因子能填满1~m这些格子 找…

Vue2 Vue3 Scoped 样式穿透

概念 主要是用于修改很多 Vue 常用的组件库&#xff08;Element, Vant, AntDesigin&#xff09;&#xff0c;虽然配好了样式但是还是需要更改其他的样式&#xff0c; 因为添加了 scoped 实现 css 模块化 就需要用到 样式穿透 &#xff0c;更改组件的样式 scoped 的原理 Vue …

route详解

一、前言 个人主页: ζ小菜鸡大家好我是ζ小菜鸡&#xff0c;让我们一起学习route。如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连) 二、什么是route Route就是用来显示、人工添加和修改路由表项目的。大多数主机一般都是驻留在只连接一台路由器的网段上。由于只有一台路…

1000个已成功入职的软件测试工程师简历经验总结:软件测试工程师简历项目经验怎么写?(含真实简历模板)

一、前言&#xff1a;浅谈面试 面试是我们进入一个公司的门槛&#xff0c;通过了面试才能进入公司&#xff0c;你的面试结果和你的薪资是息息相关的。那如何才能顺利的通过面试&#xff0c;得到公司的认可呢?面试软件测试要注意哪些问题呢?下面和笔者一起来看看吧。这里分享一…