【多线程初阶】多线程案例之阻塞式队列

news2024/11/19 23:28:44

文章目录

  • 前言
  • 1. 什么是阻塞队列
  • 2. 生产者消费者模型
    • 2.1 生产者消费者模型的优势
      • 2.1.1 解耦合
      • 2.1.2 削峰填谷
  • 3. Java 标准库中的阻塞队列
    • 3.1 生产者消费者模型
  • 4. 自己实现一个阻塞队列
  • 总结


前言

本文主要给大家讲解多线程的一个重要案例 — 阻塞式队列.

关注收藏, 开始学习吧🧐


1. 什么是阻塞队列

阻塞队列是一种特殊的队列. 也遵守 “先进先出”(First In First Out) 的原则.

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

  • 当队列满的时候, 继续入队列就会阻塞, 直到有其他线程从队列中取走元素.
  • 当队列空的时候, 继续出队列也会阻塞, 直到有其他线程往队列中插入元素.

而阻塞队列的一个典型应用场景就是 “生产者消费者模型”. 这是一种非常典型的开发模型, 作为一种处理多线程问题的方式.

2. 生产者消费者模型

生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题.

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

比如过年一家人一起包饺子. 并不是由每一个人既负责擀饺子皮又负责包饺子, 这种做法是比较低效的.

一般都是有明确分工, 比如一个人负责擀饺子皮, 其他人负责包. 在这里我们假设擀饺子皮的人就是 “生产者”, 那么包饺子的人就是 “消费者”.

擀饺子皮的人不关心包饺子的人是谁(能包就行, 无论是手工包, 借助工具, 还是机器包), 包饺子的人也不关心擀饺子皮的人是谁(有饺子皮就行, 无论是用擀面杖擀的, 还是拿罐头瓶擀, 还是直接从超市买的).

2.1 生产者消费者模型的优势

简单来说, 生产消费者模型可以减少任务切换所花费的开销, 并且减少不必要的锁竞争.

优势主要有以下两个:

  1. 解耦合
  2. 削峰填谷

2.1.1 解耦合

阻塞队列可以使生产者和消费者之间 解耦. 解耦就是降低模块之间的耦合. 现在我们来考虑一个分布式系统.
在这里插入图片描述
在这种情况下, A 是直接将外网发送来的请求转发给 B, A 和 B 之间的耦合就比较明显了.

  • 如果 B 挂了, 就可能对 A 造成很大的影响. 反过来, 如果 A 挂了, 也会对 B 造成很大影响.
  • 再添加一个服务器 C, 此时还需要对 A 的代码进行比较大的改动.

如果引入生产者消费者模型, 引入一个阻塞队列, 就能有效的解决以上问题.
在这里插入图片描述

此时, A 和 B 就可以通过阻塞队列很好地解耦合了.

  • 如果 A 或者 B 挂了, 由于他们彼此之间不会直接进行数据交换, 没有什么太大的影响.
  • 如果要新增一个服务器 C, A 也不需要进行任何代码上的修改, 直接让 C 从阻塞队列中取元素即可.

2.1.2 削峰填谷

阻塞队列就相当于一个缓冲区, 平衡了生产者和消费者的处理能力.

服务器收到的来自于用户的请求, 并不是一成不变的, 经常会因为一些突发事件而导致用户请求数目暴增. 这种场景有很多, 比如大学中的线上选课, 微博突然有某位明星公布恋情等等, 都在短时间内产生了大量客户端用户层面上的请求.

我们还是举一个分布式系统的例子.
在这里插入图片描述
此时 A 每次收到一个请求, B 也就需要立即处理一个请求. 如果 A 能够承受的压力大, B 承受的压力比 A 小的话, 此时很有可能 B 先挂了.

这个时候就可以把这些请求都放到一个阻塞队列中, 然后再由消费者线程慢慢的来处理每个支付请求.

在这里插入图片描述

当外界的请求突然增多, A 收到的请求变多了, A 就会给阻塞队列中写入更多的请求数据. 而 B 仍可以按照之前的速度来处理请求, 就不会发生被直接冲垮的情况了.

  • 这样做可以有效进行 “削峰”, 防止服务器被突然到来的一波请求直接冲垮.
  • 峰值很多时候都是暂时的, 当峰值消退的时候, A收到的请求就变少了, B 还是按照既定的速度进行 “填谷”, 也不至于太空闲.

3. Java 标准库中的阻塞队列

在 Java 标准库中内置了阻塞队列. 如果我们需要在一些程序中使用阻塞队列, 直接使用标准库中的即可. 有基于链表的, 有基于堆的, 有基于数组的.
在这里插入图片描述
基于数组的 Array 这个版本的速度要更快, 但前提是得先知道最多有多少个元素. 如果不知道有多少元素, 使用 Linked 更合适, 毕竟对于 Array 版本来说, 频繁扩容也会是一个不小的开销.

使用时要注意以下三点:

  • BlockingQueue 是一个接口. 真正实现的类是 LinkedBlockingQueue.
  • put 方法用于阻塞式的入队列, take 用于阻塞式的出队列.
  • BlockingQueue 也有 offer, poll, peek 等方法, 但是这些方法不带有阻塞特性.

接下来我们就可以基于阻塞队列来实现一个简单的生产者消费者模型.

3.1 生产者消费者模型

我们写俩个线程, 一个线程用来生产, 一个线程用来消费.

public class ThreadDemo18 {
    // 代码, 让生产者, 每隔 1s 生产一个元素.
    // 让消费者则直接消费, 不受限制.
    public static void main(String[] args) {
        // 创建一个基于链表实现的阻塞队列.
        BlockingQueue<Integer> queue = new LinkedBlockingQueue<>();

        Thread t1 = new Thread(() -> {
            while (true) {
                try {
                    int ret = queue.take();
                    System.out.println("消费元素: " + ret);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "consumer");

        Thread t2 = new Thread(() -> {
            int value = 0;
            while (true) {
                try {
                    System.out.println("生产元素: " + value);
                    queue.put(value);
                    value++;
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "producer");

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

4. 自己实现一个阻塞队列

学习了之前的基础内容, 接下来, 我们将自己实现一个阻塞队列.

  • 通过 “循环队列” 的方式来实现.
  • 使用 synchronized 进行加锁控制.
  • put 插入元素的时候, 判定如果队列满了, 就进行 wait. (注意, 要在循环中进行 wait. 被唤醒时不一定队列就不满了, 因为同时可能是唤醒了多个线程).
  • take 取出元素的时候, 判定如果队列为空, 就进行 wait. (也是循环 wait)
class MyBlockingQueue {
    // 使用一个 int 类型的数组来保存元素. 假设这里只存 int.
    private int[] items = new int[100];
    // head 指向队伍的头部
    volatile private int head = 0;
    // tail 指向队伍的尾部
    volatile private int tail = 0;
    // size 用来记录元素个数
    volatile private int size = 0;

    // 创建一个 locker 锁对象
    private Object locker = new Object();
    
    // 入队列
    public void put(int elem) throws InterruptedException {
        synchronized (locker) {
            while (size >= items.length) {
                // return;
                locker.wait();
            }
            items[tail++] = elem;
            if (tail >= items.length) {
                tail = 0;
            }
            size++;
            locker.notify();
        }
    }

    // 出队列
    public int take() throws InterruptedException {
        synchronized (locker) {
            while (size == 0) {
                // return 0;
                locker.wait();
            }
            int elem = items[head++];
            if (head >= items.length) {
                head = 0;
            }
            size--;
            locker.notify();
            return elem;
        }
    }
}

总结

✨ 本文主要讲解了阻塞队列的概念, 以及基于阻塞队列实现的生产着消费者模型, 并自己动手写了一个阻塞队列.
✨ 想了解更多的多线程知识, 可以收藏一下本人的多线程学习专栏, 里面会持续更新本人的学习记录, 跟随我一起不断学习.
✨ 感谢你们的耐心阅读, 博主本人也是一名学生, 也还有需要很多学习的东西. 写这篇文章是以本人所学内容为基础, 日后也会不断更新自己的学习记录, 我们一起努力进步, 变得优秀, 小小菜鸟, 也能有大大梦想, 关注我, 一起学习.

再次感谢你们的阅读, 你们的鼓励是我创作的最大动力!!!!!

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

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

相关文章

如何在Windows11上使用macOS Sonoma全新的慢镜屏幕保护程序

前言 macOS Sonoma是Apple macOS一个大版本的描述&#xff0c;以任何方式使用macOS Sonoma都应确保符合Apple的规定 本文假定你在搭载Intel处理器的Apple产品上通过bootcamp安装了Windows11&#xff0c;且想要让Windows11产生类似于macOS Sonoma全新的慢镜屏幕保护程序的相关…

java面试题-jvm基础知识

1 JVM组成 1.1 JVM由那些部分组成&#xff0c;运行流程是什么&#xff1f; 难易程度&#xff1a;☆☆☆ 出现频率&#xff1a;☆☆☆☆ JVM是什么 Java Virtual Machine Java程序的运行环境&#xff08;java二进制字节码的运行环境&#xff09; 好处&#xff1a; 一次编写&a…

【记录文】Android自定义Dialog实现圆角对话框

圆角的dialog还是蛮常用的&#xff0c;demo中正好用上了 自定义Dialog&#xff0c;代码中可以设置指定大小与位置 /*** author : jiangxue* date : 2023/9/25 13:21* description :圆角的矩形*/internal class RoundCornerView(context: Context,view: Int, StyleRes theme…

智能网联驾驶测试与评价工业和信息化部重点实验室“车载智能计算基础平台参考架构2.0专家研讨会”圆满结束

近日&#xff0c;智能网联驾驶测试与评价工业和信息化部重点实验室在北京市召开“车载智能计算基础平台参考架构2.0专家研讨会”&#xff0c;本次会议由智能网联驾驶测试与评价工业和信息化部重点实验室、中国软件评测中心&#xff08;工业和信息化部软件与集成电路促进中心&am…

美颜SDK哪家好?2023美颜SDK有哪些新玩法?

在当今的数字世界中&#xff0c;美颜SDK正成为一种强大的工具&#xff0c;可以帮助我们在视频直播和短视频中展现出最美的自己。美摄科技作为一家专注于美颜SDK技术的公司&#xff0c;提供了多种领先的美颜SDK相关产品&#xff0c;以满足不同用户的需求。 美摄科技的美颜SDK是一…

第一章 计算机网络与协议

文章目录 一、计算机网络的基础概念二、计算机网络分类2.1 通信子网/资源子网/网络协议2.2 网络拓补图分类2.3 按照覆盖范围分类2.4 按照交换技术分类2.5 其他分类 三、OSI参考模型3.1 应用层3.2 表示层3.3 会话层3.4 传输层3.5 网络层3.6 数据链路层3.7 物理层3.8 借助OSI模型…

众佰诚:现在开一家抖音小店还来得及吗

随着互联网的迅猛发展&#xff0c;电商行业也进入了一个全新的时代&#xff0c;其中抖音小店作为新兴的销售平台备受瞩目。然而&#xff0c;对于那些考虑开设抖音小店的人来说&#xff0c;一个重要的问题是&#xff1a;现在开一家抖音小店还来得及吗? 答案是肯定的&#xff0c…

Vulnhub-DC-8 靶机复现完整过程

Vulnhub-DC-8 靶机复现完整过程 一、环境搭建 kali的IP地址&#xff1a;192.168.200.14 DC-8的IP地址&#xff1a;192.168.200.13&#xff08;一个flag&#xff09; 靶机和攻击机处于同一个网络方式&#xff1a;nat或桥接 若出现开机错误&#xff0c;适当将dc的兼容版本改低…

纷享销客荣获“最佳用户支持与服务奖”

近日&#xff0c;e签宝第二届用户生态峰会在上海如约而至。纷享销客作为e签宝生态合作伙伴&#xff0c;荣获“最佳用户支持与服务奖”。 数字化时代&#xff0c;孤岛式、断裂式、封闭式的数字化&#xff0c;不仅无法为客户带来价值&#xff0c;对提供数字化服务的双方而言&…

latex subsection 第一段 首行取消缩进

需求&#xff1a;在\subsection 标题下的第一段&#xff0c;取消首行缩进。 &#xff08;此时直接使用 \noindent 命令&#xff0c;失效&#xff09; 环境&#xff1a;IEEE 模板 解决方案&#xff1a; 增加一个空行&#xff0c;使有效内容行变为第二行&#xff0c;然后对其…

SQLAlchemy常用数据类型

目录 SQLAlchemy常用数据类型 代码演示 代码分析 SQLAlchemy常用数据类型 SQLAlchemy 是一个Python的SQL工具库和对象关系映射(ORM)工具&#xff0c;它提供了一种在Python中操作数据库的高效方式。下面是SQLAlchemy中常用的一些数据类型&#xff1a; Integer&#xff1a;整形&…

体验亚马逊的 CodeWhisperer 感觉

CodeWhisperer 是亚马逊推出的辅助编程工具&#xff0c;在程序员写代码时&#xff0c;它能根据其内容生成多种代码建议。 CodeWhisperer 目前已支持近10几种语言&#xff0c;我是用 java 语言&#xff0c;用的开发工具是 idea&#xff0c;说一下我用的情况。 亚马逊云科技开发…

四川玖璨电子商务有限公司正规吗?

​近年来&#xff0c;随着社交平台的兴起&#xff0c;带货已经成为了一种新型商业模式。抖音作为中国最大的短视频平台之一&#xff0c;也成为了众多商家进行带货销售的重要渠道。而在这个过程中&#xff0c;公司如四川玖璨电子商务有限公司也逐渐涌现出来&#xff0c;为广大用…

什么是 Redis?

Redis 是一种基于内存的数据库&#xff0c;对数据的读写操作都是在内存中完成的&#xff0c;因此读写速度非常快&#xff0c;常用于缓存&#xff0c;消息队列&#xff0c;分布式锁等场景。 Redis 提供了多种数据类型来支持不同的业务场景&#xff0c;比如 String(字符串)、Has…

Spring源码解析—— IOC默认标签解析(下)

正文 在上一篇我们已经完成了从xml配置文件到BeanDefinition的转换&#xff0c;转换后的实例是GenericBeanDefinition的实例。本文主要来看看标签解析剩余部分及BeanDefinition的注册。 默认标签中的自定义标签解析 在上篇博文中我们已经分析了对于默认标签的解析&#xff0…

vue 组件通信

vue 组件通信 mytitile 是变量 title 是形参 回调函数

RocketMQ 线上问题处理

一、发送超时后&#xff0c;重试发送 1.1、问题 生产者发送超时&#xff0c;进行重试发送。 1.2、解决 消费端做幂等处理。

在使用SpringBoot时遇到的异常总结(持续更新...)

文章目录 异常MyBatisjava.sql.SQLIntegrityConstraintViolationException: Cannot add or update a child row: a foreign key constraint failsMybatis-Plus查询语句无故自动加条件Mybatis No enum constant org.apache.ibatis.type.JdbcType.TEXT 参考文献 因为平时在写Spri…

pandas使用---Series/DataFrame

借鉴 Pandas 常用函数 | 菜鸟教程Pandas 常用函数 以下列出了 Pandas 常用的一些函数及使用实例&#xff1a; 读取数据 函数说明 pd.read_csv(filename)读取 CSV 文件&#xff1b; pd.read_excel(filename)读取 Excel 文件&#xff1b; pd.read_sql(query, connection_object)…

ElasticSearch从入门到精通(二)

ElasticSearch 高级操作 bulk批量操作 批量操作-脚本 #批量操作 #1.删除5号 #新增8号 #更新2号 name为2号 POST _bulk {"delete":{"_index":"person1","_id":"5"}} {"create":{"_index":"person…