[线程]阻塞队列

news2024/11/15 7:01:43

文章目录

  • 阻塞队列
    • 生产者消费者模型
    • 通过BlockingQueue理解阻塞队列
    • 自己实现阻塞队列

阻塞队列

我们之前学的队列, 其实是最基础的队列, 实际开发中, 针对队列还有很多种变种

  1. 普通队列
  2. 优先级队列
  3. 阻塞队列
    先进先出, 线程安全, 并且带有阻塞功能
    阻塞功能指:
    如果队列为空, 尝试出队列, 出队列操作就会阻塞, 一直阻塞到队列不为空为止
    如果队列为满, 尝试入队列, 入队列操作也会阻塞, 一直阻塞到队列不满为止
  4. 消息队列
    不是普通的先进先出, 而是通过topic这样的参数来对数据进行分类, 出队列的时候, 指定topic, 每个topic下的数据先进先出, 也有阻塞性质

消息队列这样的数据结构, 在实际开发中, 经常会把这样的数据结构封装成单独的服务器程序, 单独部署, 这样的服务器程序, 同时也称为消息队列

消息队列能够起到的作用就是实现==“生产者消费者模型”==

生产者消费者模型

阻塞队列, 也可以实现生产者消费者模型:
如果是在一个进程内, 直接使用阻塞队列, 即可实现生产者消费者模型
如果在分布式系统中, 需要使用单独部署的消息队列服务器, 实现生产者消费者模型

生产者消费者模型, 在开发中主要有两方面的意义:
1. 能够让程序进行解耦
在这里插入图片描述
如果让A直接调用B, 意味着A的代码就要包含很多和B相关的逻辑, B的代码也会包含和A相关的逻辑
彼此之间有了一定的耦合
一旦A做出修改 或 A出了bug, 就可能影响到B, 反之亦然

在这里插入图片描述
站在A的视角, 不知道B的存在, 只关心和队列的交互
站在B的视角, 不知道A的存在, 只关心和队列的交互
实现了程序的解耦合
此时, 对A做出修改 或 A出了bug, 不太容易影响到B, 反之亦然
未来如果在引入C, 也让A访问C, 那么A中不需要修改任何代码, 直接在队列里读取C的数据即可, 可以提高代码的可扩展能力

2. 能够使程序"削峰填谷"

客户端发来的请求, 个数多少, 我们是没办法提前预知的, 遇到某些突发事件, 就可能导致客户端给服务器发来的请求激增
在这里插入图片描述
正常情况下, A收到一个客户端的请求, 就同样要请求B一次
如果A收到的请求激增了, 那么B的请求也会激增
但是由于A做的工作比较简单, 消耗的资源少, 但是B的工作复杂, 消耗的资源多, 一旦请求量大, B就容易挂

那么引入消息队列, 就可以保证, 无论A给队列写多块, B都可以按照固有的节奏来消费数据, B的节奏就不一定完全跟着A了

通过BlockingQueue理解阻塞队列

在这里插入图片描述
BlockingQueue 是一个接口, 表示阻塞队列
有三个类实现了这个接口:
在这里插入图片描述
在这里插入图片描述
阻塞队列只需要考虑, 入队列和出队列即可, 阻塞队列没有"取队首操作"
之前普通队列使用的是offer和poll, 但这组方法是没有阻塞功能的
阻塞队列使用的方法是: put 和 take

ArrayBlockingQueue, 必须要传入capacity, 作为数组的最大容量
如果队列为满, 进行put就会等待阻塞, 直到有现成take走一个元素
如果队列为空, 进行take就会等待阻塞, 直到有线程put一个元素
put和take操作都需要抛InterruptedException, 阻塞过程中, 如果其他线程尝试使用interrupt来终止被阻塞的线程, 此时put就能够返回, 并抛出异常
在这里插入图片描述
第二次take时, 队列中已经没有元素, 则线程就会进入阻塞状态
在这里插入图片描述
实现生产者消费者模型:

 public static void main(String[] args) {
        BlockingQueue<Integer> queue = new ArrayBlockingQueue<>(1000);
        //生产者
        Thread t1 = new Thread(() -> {
            int i = 1;
            while(true){
                try {
                    queue.put(i);
                    System.out.println("t1生产: " + i);
                    i++;
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

            }
        });
        //消费者
        Thread t2 = new Thread(() -> {
            int elem = 0;
            while(true){
                try {
                    elem = queue.take();
                    System.out.println("t2消费: " + elem);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        t1.start();
        t2.start();
    }

上述代码, 我们让t1每秒钟生产一个, 此时t2只能阻塞等待, 等t1生产完后, t2才能消费
在这里插入图片描述
下面我们让t2每秒钟消费一次, 那么t1生产到队列满后, 只能等待t2消费完, t1才能生产
在这里插入图片描述

自己实现阻塞队列

用数组模拟队列, 记录head和tail下标, 用size记录数组中的有效值(用volatile修饰, 保证线程安全)
入队列:
在这里插入图片描述

  1. 给put操作加锁, 保证线程安全
  2. **使用wait操作, 最好搭配while循环使用 **
    因为wait不仅仅能被notify和notifyAll唤醒, 还可能被其他操作唤醒, 例如interrupt
    如果我们使用trycatch, 捕获到异常后, 如果没有继续抛出异常, 后面的代码就会继续执行, 进程不会结束, 此时就会覆盖掉本来的元素
    此处我们使用的是throws, 进程不会继续执行
    如果搭配while, 可以保证如果wait被其他线程唤醒, 能够再次条件判断, 判断是否真的要继续执行
  3. 成功put元素后, 记得使用notify唤醒take的阻塞

出队列:
在这里插入图片描述
完整代码:

class MyBlockingQueue{
    private String[] elems = null;
    private volatile int head;
    private volatile int tail;
    private volatile int size = 0;

    public MyBlockingQueue(int capacity){
        elems = new String[capacity];
    }
    //入队列
    public void put(String elem) throws InterruptedException {
        synchronized(this){
            while(size >= elems.length){
                //队列满了, 进行阻塞等待
                this.wait();
            }
            //将新的元素加入到队列中
            elems[tail] = elem;
            tail++;
            //循环队列
            if(tail >= elems.length){
                tail = 0;
            }
            //更新size的值
            size++;
            //唤醒take的阻塞
            this.notify();
        }
    }
    //出队列
    public String take() throws InterruptedException {
        synchronized (this){
            while(size == 0){
                //队列为空, 进行阻塞等待
                this.wait();
            }
            //将队首元素返回给elem
            String elem = elems[head];
            tail++;
            //循环队列
            if(head >= elems.length){
                head = 0;
            }
            //更新size的值
            size--;
            //唤醒put的阻塞
            this.notify();
            return elem;
        }
    }
}
public class Demo25 {
    public static void main(String[] args) {
        MyBlockingQueue queue = new MyBlockingQueue(1000);
        //生产者
        Thread t1 = new Thread(() -> {
            int i = 1;
            String elem = "aaa";
            while(true){
                try {
                    queue.put(elem);
                    System.out.println("t1生产: " + i);
                    i++;
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }

            }
        });
        //消费者
        Thread t2 = new Thread(() -> {
            while(true){
                try {
                    String elem = queue.take();
                    System.out.println("t2消费: " + elem);
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        t1.start();
        t2.start();
    }
}

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

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

相关文章

23种设计模式之模板模式

一.什么是模板模式 ‌‌模板模式是一种行为型设计模式&#xff0c;它定义了一个算法的骨架&#xff0c;而将一些步骤留给子类实现。‌这种模式允许子类在不改变算法结构的基础上&#xff0c;重新定义算法的某些步骤。模板模式属于行为型设计模式&#xff0c;主要用于处理那些需…

excel透视图、看板案例(超详细)

一、简介 Excel透视图&#xff08;Pivot Table&#xff09; 功能&#xff1a;透视图是一种强大的数据分析工具&#xff0c;用于汇总、分析和展示数据。它允许用户对数据进行重新排列和分类&#xff0c;从而更容易发现数据中的模式和趋势。用途&#xff1a;可以用来生成动态报表…

python07-单元测试框架unittest1-3

当测试用例数量增加&#xff0c;一个一个执行效率低下&#xff0c;需要将工程下的&#xff0c;case收集并按顺序执行将对应的代码放入run_tests.py run_tests.py&#xff1a;运行程序目的 收集所有的测试用例执行生成测试报告 运用测试用例的收集器或测试用例的加载器 7 Tes…

2.4梯度下降与量化策略优化

1. 梯度下降法的基本原理 欢迎来到“梯度下降”的世界&#xff01;听上去有点像在爬山对吧&#xff1f;其实&#xff0c;这个算法的灵感确实来自爬山。想象你在一个山谷中迷路了&#xff0c;周围雾蒙蒙的&#xff0c;看不清楚路&#xff0c;只能摸着石头一步一步往下走。每走一…

短效ip—互联网利器

《瞬息万变&#xff1a;短效IP在网络世界的奇幻之旅》 在浩瀚无垠的数字宇宙中&#xff0c;互联网如同一条奔腾不息的河流&#xff0c;携带着无数创新与技术的浪花。在这片日新月异的疆域里&#xff0c;短效IP以其独有的魅力&#xff0c;悄然成为网络探险家们手中的魔法钥匙。它…

编译原理概述

编译原理概述 编译原理是计算机科学的重要领域&#xff0c;主要研究编译器如何将高级编程语言转换为机器可执行代码。编译器的工作流程可以分为多个阶段&#xff0c;每个阶段都有特定的功能和目标。理解编译原理对于编写高效的代码、优化程序性能以及开发新语言或编译器非常重…

Java 线程实现暂停、中止

需求&#xff1a;用户可以开启任务&#xff0c;暂停任务和中止任务。 用户开启任务后&#xff0c;可以随时暂停或者中止。暂停后又可以回到原进度继续运行。 这里写目录标题 demo版-使用废弃的stop、suspend、resume实现为什么废弃了&#xff1f;不用stop&#xff0c;如何销毁线…

MySQL5.7.36之主从复制部署安装-centos7

主库是192.168.31.209:3306 从库是192.168.31.210:3308、192.168.31.209:3307、192.168.31.210:3309、192.168.31.211:3310、192.168.31.211:3311 切记&#xff1a;不管是主库还是从库&#xff0c;server_id一定不能重复 1、主库创建复制账号及授权 create user repl% iden…

Linux驱动开发基础(IRDA 红外遥控模块)

所学来自百问网 目录 1.红外遥控简介 2.硬件设计 3.软件设计 4. 示例代码 4.1 驱动代码 4.2 Makefile 4.3 实验效果 1.红外遥控简介 红外遥控被广泛应用于家用电器、工业控制和智能仪器系统中&#xff0c;像我们熟知的有电视机盒子遥控器、空调遥控器。红外遥控器系统…

分类预测|基于灰狼GWO优化BP神经网络的数据分类预测Matlab程序GWO-BP 含基础BP对比模型

分类预测|基于灰狼GWO优化BP神经网络的数据分类预测Matlab程序GWO-BP 含基础BP对比模型 文章目录 一、基本原理1. 灰狼优化算法&#xff08;GWO&#xff09;简介GWO的基本步骤 2. BP神经网络简介**BP网络的基本结构****训练过程** 3. GWO-BP分类预测的结合**结合流程** 4. GWO-…

苹果mac数据恢复概率大吗 mac数据恢复专业软件哪个好用

一般情况下&#xff0c;当我们把电脑中的数据删掉后&#xff0c;都会保存在回收站里面&#xff0c;但如果回收站被清空了或者数据在回收站中没有找到的话&#xff0c;那么&#xff0c;之前被删掉的数据还能恢复吗&#xff1f;恢复的概率有多大呢&#xff1f; 答案是可以的&…

Hive 案例分析(B站用户行为大数据分析)

Hive 案例分析&#xff08;B站用户行为大数据分析&#xff09; 一、案例需求二、设计数据表结构2.1 user 表结构2.2 video 表结构 三、创建数据表3.1 创建 video 数据库3.2 创建外表3.1.2 创建 external_user3.1.3 创建 external_video 3.2 创建内表3.2.1 创建 orc_user3.2.2 创…

Atlas阿特拉斯wordpress主题

Atlas阿特拉斯是一个专为WordPress平台设计的多功能主题&#xff0c;该主题由简站wordpress主题开发&#xff0c;旨在为用户提供一个强大而灵活的工具&#xff0c;以构建各种类型的网站。以下是对Atlas阿特拉斯WordPress主题的简介&#xff1a; Atlas阿特拉斯WordPress主题简介…

谷歌账号被停用了怎么申诉?申诉了好多天没有收到回复怎么办?申诉了很多次都被拒了怎么办?只有一个办法

这段时间似乎谷歌的风控措施又变严了&#xff0c;许多新账号被封&#xff08;尤其是买来的账号或者新注册的账号&#xff09;&#xff0c;甚至有一些使用了一年以上的老账号“莫名其妙地”也封了。 注&#xff1a;这里的封号是谷歌用户的一个通俗的说法&#xff0c;实际上指的…

【AIGC】MimicMotion:姿态引导的高质量人体运动视频生成技术

资源 论文&#xff1a;https://arxiv.org/pdf/2406.19680 github:https://github.com/Tencent/MimicMotion comfyui:https://github.com/kijai/ComfyUI-MimicMotionWrapper 核心要点 1. confidence-aware pose guidance可以确保高质量视频和时间维度上的帧与帧之间的平滑 …

MySQL的索引原理及使用

索引模型&#xff08;基础数据结构&#xff09; 索引模型&#xff1a;哈希表、有序数组、搜索树&#xff0c;这里的模型是指索引的底层实现的基本数据结构&#xff0c;Mysql中不同的引擎对于索引的实现结构说的即是索引模型。 有序数组 有序数组这个在数据结构中是最基础的结…

【开关电源】数字交错式升压功率因数校正解析(1)

文章目录 Digital Interleaved PFC 链接: TIDM-02010 Dual motor control with digital interleaved PFC for HVAC reference design Digital Interleaved PFC 电路的主干部分不做过多介绍&#xff0c;默认读者已清楚知道PFC电路的控制原理。 这里想要探讨一下D5和L6的作用。 …

Go开发运维:Go项目工程化进行初始配置

目录 一、实验 1.环境 2.初始化Go项目 3.Go项目工程化 4. 导入Gin包 5.下载viper与logrus依赖 二、问题 1.Windows如何进行go的版本升级 2. Go mod常用命令 3.下载Gin依赖失败 一、实验 1.环境 &#xff08;1&#xff09;主机 表1 主机 系统 软件版本备注Windows1…

JUC-指令有序性

指令重排 JVM 会在不影响正确性的前提下&#xff0c;可以调整语句的执行顺序&#xff0c;思考下面一段代码 static int i; static int j; // 在某个线程内执行如下赋值操作 i ...; j ...; 可以看到&#xff0c;至于是先执行 i 还是 先执行 j &#xff0c;对最终的结果不…

大数据技术之HBase优化(5)

目录 HBase 优化 RowKey 设计 实现需求 1 实现需求 2 添加预分区优化 参数优化 JVM 调优 HBase 使用经验法则 HBase 优化 RowKey 设计 一条数据的唯一标识就是 rowkey&#xff0c;那么这条数据存储于哪个分区&#xff0c;取决于 rowkey 处于哪个预分区的区间内。设计 rowkey …