带你手搓阻塞队列——自定义实现

news2024/12/26 20:59:38

🌈🌈🌈今天给大家分享的是——阻塞队列的自定义实现,通过自定义实现一个阻塞队列,可以帮助我们更清晰、更透彻的理解阻塞队列的底层原理。

清风的CSDN博客

🛩️🛩️🛩️希望我的文章能对你有所帮助,有不足的地方还请各位看官多多指教,大家一起学习交流!🛩️🛩️🛩️

✈️✈️✈️动动你们发财的小手,点点关注点点赞!在此谢过啦!哈哈哈!✈️✈️✈️

 

目录

一、阻塞队列的作用 

二、阻塞队列实现 

2.1 普通队列实现 

2.1.1 构造方法 

2.1.2 入队列

 2.1.3 出队列

2.2 阻塞队列实现

2.2.1 保证线程安全 

2.2.2 保证内存可见性 

 2.2.3 阻塞功能的实现

三、基于自定义阻塞队列,模拟生产者消费者模型


 

一、阻塞队列的作用 

 一个分布式系统中,会经常出现这样的情况:有的机器能承担的压力更大,有的能承担的压力更小:

如果按照生产者消费者模型,那就另当别论了。

假设此时通过队列来让A和B进行交互:

 

二、阻塞队列实现 

2.1 普通队列实现 

在实现阻塞队列之前,我们先把普通的队列(基于数组的循环队列)进行一个简单的实现,然后通过进一步的改进,把普通的队列改造成一个阻塞队列。

class MyBlockingQueue{
    private int[] items;

    private int head = 0;//队列头指针

    private int tail = 0;//队列尾指针

    private int size = 0;//队列当前元素个数

    public MyBlockingQueue(){}
    //入队列
    public void put(int elem){}

    //出队列
    public int take(){}

}

2.1.1 构造方法 

 public MyBlockingQueue(){
        this.items = new int[100];
    }

2.1.2 入队列

   //入队列
    public void put(int elem){
        if (size >= items.length){
            return;
        }
        items[tail] = elem;
        if (tail >= items.length){//判断尾指针是否到达末尾
            tail = 0;
        }
        tail++;
        size++;
    }

 2.1.3 出队列

    //出队列
    public int take(){
        if(size == 0){
            return -1;
        }
        int elem = items[head];
        if (head >= items.length){
            head = 0;
        }
        head++;
        size--;
        return elem;
    }

2.2 阻塞队列实现

现在,我们就把上面的队列改造成阻塞队列。

2.2.1 保证线程安全 

在当前的代码下,如果是多线程的情况,调用put或者take,这两个方法中都涉及到了对变量的修改,这样就会出现线程安全问题。这就需要我们进行加锁。

    //入队列
    public void put(int elem){
        synchronized (this){
            if (size >= items.length){
                return;
            }
            items[tail] = elem;
            if (tail >= items.length){
                tail = 0;
            }
            tail++;
            size++;
        }
    }
    //出队列
    public int take(){
        synchronized (this){
            if(size == 0){
                return -1;
            }
            int elem = items[head];
            if (head >= items.length){
                head = 0;
            }
            head++;
            size--;
            return elem;
        }
    }

2.2.2 保证内存可见性 

光加锁就够吗?我们可以看到,多线程的情况下,不光是对变量进行修改,还有读操作等等,那就有可能出现一个线程在读,另外一个线程在修改,这个读的线程没有读到。所以,此处除了加锁之外,还需要考虑内存可见性问题。也就是说,当其他线程进行修改的时候,我们要保证当前线程可以读到这个修改,所以我们把变量加上volatile关键字。

    volatile private int head = 0;
    volatile private int tail = 0;
    volatile private int size = 0;

 2.2.3 阻塞功能的实现

解决了上述问题后,我们就需要考虑一下如何实现阻塞功能了。

实现阻塞有两方面:

  • 当队列满的时候,再进行put(入队),就会产生阻塞。阻塞到队列中元素出队后,就去唤醒当前因队列满而被阻塞的状态。
  • 当队列空的时候,再进行take(出队),就会产生阻塞。阻塞到队列中有元素入队时,去唤醒当前因队列空而被阻塞的状态。 
    //入队列
    public void put(int elem) throws InterruptedException {
        synchronized (this){
            while (size >= items.length){
                //队列满了
                //return;
                this.wait();
            }
            items[tail] = elem;
            if (tail >= items.length){
                tail = 0;
            }
            tail++;
            size++;
            //成功入队
            this.notify();//唤醒因队列空而被阻塞的状态
        }
    }

    //出队列
    public int take() throws InterruptedException {
        synchronized (this){
            while (size == 0){
                //队列空
                //return -1;
                this.wait();
            }
            int elem = items[head];
            if (head >= items.length){
                head = 0;
            }
            head++;
            size--;
            this.notify();//使用这个notify唤醒队列满的阻塞状态
            return elem;
        }
    }

}

 好了,经过上面的改进,我们就已经实现了一个简单的阻塞队列,下面是改进后的完整代码:

class MyBlockingQueue{
    private int[] items;
    volatile private int head = 0;
    volatile private int tail = 0;
    volatile private int size = 0;
    public MyBlockingQueue(){
        this.items = new int[100];
    }
    //入队列
    public void put(int elem) throws InterruptedException {
        synchronized (this){
            while (size >= items.length){
                //队列满了
                //return;
                this.wait();
            }
            items[tail] = elem;
            if (tail >= items.length){
                tail = 0;
            }
            tail++;
            size++;
            //成功入队
            this.notify();//唤醒因队列空而被阻塞的状态
        }
    }

    //出队列
    public int take() throws InterruptedException {
        synchronized (this){
            while (size == 0){
                //队列空
                //return -1;
                this.wait();
            }
            int elem = items[head];
            if (head >= items.length){
                head = 0;
            }
            head++;
            size--;
            this.notify();//使用这个notify唤醒队列满的阻塞状态
            return elem;
        }
    }

}

三、基于自定义阻塞队列,模拟生产者消费者模型

实现阻塞队列之后,我们利用阻塞队列简单模拟一下生产者消费者模型:

    public static void main(String[] args) {
        MyBlockingQueue queue = new MyBlockingQueue();
        //生产者线程
        Thread product = new Thread(()->{
            int count = 0;
            while (true){
                try {
                    queue.put(count);
                    System.out.println("生产元素:>"+count);
                    count++;
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        //消费者线程
        Thread consummer = new Thread(()->{
            while (true){
                try {
                    int elem = queue.take();
                    System.out.println("消费元素:>"+elem);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        product.start();
        consummer.start();
    }

 运行结果:


🌈🌈🌈好啦,今天的分享就到这里!

🛩️🛩️🛩️希望各位看官读完文章后,能够有所提升。

🎉🎉🎉创作不易,还希望各位大佬支持一下!

✈️✈️✈️点赞,你的认可是我创作的动力!

⭐⭐⭐收藏,你的青睐是我努力的方向!

✏️✏️✏️评论:你的意见是我进步的财富!

 

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

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

相关文章

C++stack

目录 1.什么是stack 2.容器适配器 3.stack的使用 top push pop 4.模拟实现stack 1.什么是stack 1. stack是一种容器适配器,专门用在具有后进先出操作的上下文环境中,其删除只能从容器的一端进行 元素的插入与提取操作。(后进先出) 2. stack是作为容…

进程间通信方式——管道

进程间通信方式——管道 1、管道2、匿名管道2.1 创建匿名管道2.2 进程间通信 3、有名管道3.1 创建有名管道3.2 进程间通信 4、管道的读写行为 原文链接 1、管道 管道的是进程间通信(IPC - InterProcess Communication)的一种方式,管道的本质…

深入 C 语言和程序运行原理 实战项目代码在CentOS 7上编译

cat /etc/redhat-release看到操作系统的版本是CentOS Linux release 7.6.1810 (Core),uname -r可以看到内核版本是3.10.0-957.21.3.el7.x86_64。 安装gtest 参考博客《使用gtest和lcov测试代码覆盖率》 wget https://github.com/google/googletest/archive/refs/…

YOLOv5项目实战(5)— 算法模型优化和服务器部署

前言:Hello大家好,我是小哥谈。近期,作者所负责项目中的算法模型检测存在很多误报情况,为了减少这种误报情况,作者一直在不断优化算法模型。鉴于此,本节课就给大家详细介绍一下实际工作场景中如何去优化算法模型和进行部署,另外为了方便大家进行模型训练,作者在文章中提…

Promise的resolve和reject方法(手写题)

1.resolve 2.reject 3.手写 1.resolve //构造函数上添加 resolve 方法 Promise.resolve function (value) {return new Promise((resolve, reject) > {if (value instanceof Promise) {value.then((val) > {resolve(val)},(err) > {reject(err)})} else {resolve(v…

国内首所国际职业培训学院落户深圳盐田揭幕开业

11月26日,中科国药•中科大有大健康上市企业孵化平台迎来了国内首所国际职业学院——深圳市盐田区国际职业培训学院的正式落成与揭幕仪式。中科大有高新科技有限公司董事长、长江商学院MBA\FMBA金融导师、深圳市中科国药生物医药研究院理事长、深圳市盐田区国际职业…

HarmonyOS到底有哪些独特之处?你真正了解鸿蒙多少!

鸿蒙系统太炸裂了💥我已经后悔了😭后悔没早点学习鸿蒙 HarmonyOS 概念,系统定位 1:鸿蒙系统是由华为公司自主研发的全球化开放源代码操作系统,它具有以下特别之处: 2:分布式架构:…

华为鸿蒙工程师成“热门”!最高年薪160万,只看技术不看年龄

引言: 今天,在互联网行业,超过30岁的工程师往往被认为是“码农”的生命周期终点。然而,华为鸿蒙系统的崛起,却再次给予了这些35岁以上的工程师们第二春的机会。国内一线互联网公司纷纷向鸿蒙系统靠拢,导致…

c语言练习13周(1~5)

输入任意整数n求以下公式和的平方根。 读取一系列的整数 X,对于每个 X,输出一个 1,2,…,X 的序列。 编写double fun(int a[M][M])函数,返回二维数组周边元素的平均值,M为定义好的符号常量。 编写double fun(int a[M])函…

设计模式-结构型模式之桥接设计模式

文章目录 三、桥接模式 三、桥接模式 桥接模式(Bridge)是用于把抽象化与实现化解耦,使得二者可以独立变化。它通过提供抽象化和实现化之间的桥接结构,来实现二者的解耦。 这种模式涉及到一个作为桥接的接口,使得实体类…

Unity 使用Horizontal Layout Group和Toggle制作多个水平开关按钮实现自动排列和单个点击放大后的自动排列。

Unity的布局组件Horizontal Layout Group是很好用的,当然也包括其它布局组件也一样好用。 比如要实现多按钮开关自动水平排列,那么就可以使用它了。 首先我们为按钮创建个父物体(我这里使用了Scroll View中的Content作为父物体)…

elementUI实现根据屏幕大小自适应换行,栅格化布局

需求: 默认一行展示4个卡片;当屏幕小于某个大小的时候,一行展示3个卡片;再小就展示2个;以此类推,最小就展示1个。 效果卡片样式如下图: 默认一行4个 屏幕缩小到某个阈值,一行展示…

11.26电梯控制器设计分析

项目三 电梯控制器设计(*****) 设计一个多楼层的电梯控制器系统,并能在开发板上模拟电梯运行状态。可以利用按键作为呼叫按键,数码管显示电梯运行时电梯所在楼层,led灯显示楼层叫梯状态。 就是初始默认在1楼&#xff0…

【LeetCode:1657. 确定两个字符串是否接近 | 计数 + 阅读理解】

🚀 算法题 🚀 🌲 算法刷题专栏 | 面试必备算法 | 面试高频算法 🍀 🌲 越难的东西,越要努力坚持,因为它具有很高的价值,算法就是这样✨ 🌲 作者简介:硕风和炜,…

Kaggle-水果图像分类银奖项目 pytorch Densenet GoogleNet ResNet101 VGG19

一些原理文章 卷积神经网络基础(卷积,池化,激活,全连接) - 知乎 PyTorch 入门与实践(六)卷积神经网络进阶(DenseNet)_pytorch conv1x1_Skr.B的博客-CSDN博客GoogLeNet网…

Web 实时消息推送

Web 实时消息推送详解 什么是消息推送? 推送的场景比较多,比如有人关注我的公众号,这时我就会收到一条推送消息,以此来吸引我点击打开应用。 消息推送通常是指网站的运营工作等人员,通过某种工具对用户当前网页或移…

最小化安装 Neokylin7.0 用于搭建 Hadoop 集群

文章目录 环境搭建背景虚拟机创建和环境配置安装过程注意事项虚拟机设置软件选择KOUMP系统分区网络和主机名打开以太网,并记录信息配置 IPv4修改主机名 创建用户 hadoop完全分布式搭建-CSDN博客 环境搭建背景 为什么不从hadoop100或者hadoop101开始,而是…

引领数据趋势:2023年最值得关注的十大ETL数据集成工具

在这个数据至上的时代,对于以数据为驱动的组织来说,建立一个信息集中的强大源头是成功的关键。众多企业依靠ETL工具来管理和理解它们的数据。 ETL,即提取(Extract)、转换(Transform)、加载&…

matlab基于线性二次调节器(LQR)法实现机器人路径规划可变轨迹跟踪

1、内容简介 略 可以交流、咨询、答疑 2、内容说明 基于线性二次调节器(LQR)法实现机器人路径规划可变轨迹跟踪 3、仿真分析 略 load path.mat %% 轨迹处理 % 定义参考轨迹 refPos_x path(:,1); refPos_y path(:,2); refPos [refPos_x, refPos_y];% 计算航向角和曲率 …

python的制图

测试数据示例: day report_user_cnt report_user_cnt_2 label 2023-10-01 3 3 欺诈 2023-10-02 2 4 欺诈 2023-10-03 6 5 欺诈 2023-10-04 2 1 正常 2023-10-05 4 3 正常 2023-10-06 4 4 正常 2023-10-07 2 6 正常 2023-10-08 3 7 正常 2023-10-09 3 12 正常 2023-…