多线程案例——阻塞队列

目录

一、阻塞队列

1. 生产者消费者模型

(1)解耦合

(2)“削峰填谷”

2. 标准库中的阻塞队列

3. 自己实现一个阻塞队列(代码)

4. 自己实现生产者消费者模型(代码)


一、阻塞队列

   阻塞队列同样是一个符合先进先出的队列,相比于普通队列,阻塞队列还有其他的功能:

  • 1. 线程安全
  • 2. 产生阻塞效果
  • (1)如果队列为空,尝试出队列,就会出现阻塞,阻塞到队列不为空为止;
  • (2)如果队列满了,尝试入队列,就会出现阻塞,阻塞到队列不为满为止。

   基于上述特点的阻塞队列,我们就可以实现“生产者消费者模型”。(处理多线程问题的一个典型方式)。

1. 生产者消费者模型

   生产者消费者模型:通过一个容器(阻塞队列),来解决生产者消费者之间的强耦合问题。(生产者生产商品,消费者购买商品)

   开发实际生活中,使用的 阻塞队列 并不是一个简单的数据结构,而是一组专门的服务器程序。里面也不仅仅包含了阻塞队列的功能,还有其他的功能。这样的队列叫做 “消息队列” 

(1)解耦合

   假设现在有两个服务器 A 和 B ,A 作为入口服务器(直接接收用户的网络请求),B 作为应用服务器(给 A 提供服务)。

假设不使用 生产者消费者模型:

 即生产者和消费者之间直接进行沟通,则具有 强耦合性

强耦合性 体现在:

当开发 A 代码的时候就得充分了解 B 提供的一些接口,开发 B 代码的时候也得充分了解到 A 是怎么调用的。当我们想把 B 换成 C 时,A 的代码就得发生较大的改动,并且如果 B 崩溃了,也可能导致 A 也崩溃。 

   因此强耦合性是一种不好的现象。 而生产者消费者模型刚好可以降低这里的耦合,即模块和模块之间的联系尽可能的少。

   当 A 给 B 发送请求时,A 先将请求写到阻塞队列中,然后 B 再从阻塞队列中取出请求。当 B 返回结果时,先把结果放到阻塞队列中,然后 A 再从阻塞队列中取出结果。

  • 请求:A 是生产者,B 是消费者
  • 响应:A 是消费者,B 是生产者
  • 阻塞队列:作为交易场所

   使用了生产者消费者模型后,A 只需要关注如何和队列进行交互,不需要认识 B ,B 也是如此。当 B 崩溃时,对 A 没有影响。A 崩溃时, B 也没有影响。相应的,如果将 B 换做 C ,A 也完全感知不到。 就达到了解耦合的目的。

(2)“削峰填谷”

   使用生产者消费者模型,能够对于请求进行“削峰填谷”。(类似水库 -> 当雨季时,可以利用水库正常为稻田送水;当旱季时,打开水库,同样可以保证为稻田正常送水)

   还是使用上述 A 和 B 服务器的例子。假设我们没有使用生产者消费者模型:

削峰:

   当有大量请求时,A 服务器作为入口服务器,计算量轻,不会造成太大影响。而因为强耦合性,B 也会突然暴涨,B 作为应用服务器,计算量比 A 大得多,可能就直接造成崩溃了。

   如果使用了生产者消费者模型,遇到请求暴涨的情况:

   A 的请求暴涨,导致了阻塞队列的暴涨,因为阻塞队列只是存储数据,因此没有太大影响。B 这边依然按照原来的速度消费数据,不会因为 A 的暴涨而引起暴涨。因此 B 就被保护起来了,不会因为这种请求暴涨而崩溃,即 削峰 。(让 B 感受不到)

填谷:

   当 A 的请求很少,而 B 依然会按照原来的速度消费数据。虽然 A 的请求少,但是阻塞队列中还有挤压的数据,就能够让 B 正常运行。

2. 标准库中的阻塞队列

   Java标准库中自带了阻塞队列,当我们编写代码时需要使用阻塞队列时,直接使用即可。

  • BlockingQueue 是一个接口,真正实现的类时 LinkedBlockingQueue。(类似List,LinkedList和ArrayList)
  • put:阻塞式出队列
  • take:阻塞式出队列
  • (也有 offer、poll、peek等普通队列有的方法,但是这些方法不带有阻塞特性)
public class Demo1 {
    public static void main(String[] args) throws InterruptedException {
        BlockingQueue<String> queue = new LinkedBlockingDeque<>();
        //阻塞式入队列,如果队列满,就会阻塞
        queue.put("jiu");
        //阻塞式出队列,如果队列空,就会阻塞
        System.out.println(queue.take());   //结果为 jiu
    }
}

3. 自己实现一个阻塞队列(代码)

   要实现一个阻塞队列:一个普通队列 + 线程安全 + 阻塞功能 。

① 实现队列(先进先出)可以通过链表或者数组,这里使用数组。——> 循环队列(队首head 和 队尾 tail)

  • 入队列:把新元素放到 tail 的位置,tail++
  • 出队列:取出 head 位置的元素,head++
  • 循环队列:当 head 或者 tail 到达数组末尾时,就需要从头开始(= 0),重新循环。
  • 判空 / 满:要实现循环队列,这是一个重要问题。我们用 变量 size 记录元素的个数,进行判断。

② 实现线程安全。因为在方法中全是共享变量,所以可以直接对整个方法进行加锁,也可以对整个代码块进行加锁,同时专门设置一个锁对象locker。

③ 实现阻塞效果。利用 wait 和 notify 来实现。使用哪个对象加锁就要用哪个对象 wait

  • 对于 put 来说,阻塞条件:队列为满。而 put 中的 wait 要由 take 来唤醒。(take 成功了队列就不满了)
  • 对于 take 来说,阻塞条件:队列为空。而 take 中的 wait 要由 put 来唤醒。(put 成功了队列就不为空了)

   如果有人等待,notify 可以唤醒;如果没人等待,notify 也没有任何影响。

//假设队列中存放的是整型元素
class MyBlockingQueue {
    private int[] array = new int[1000];
    private int size = 0;
    private int head = 0;
    private int tail = 0;

    private Object locker = new Object();

    //阻塞式入队列
    public void put(int value) throws InterruptedException {
        synchronized(locker) {
            if (size == array.length) {
                //说明队列满了,无法再入队列了
                //所以进入阻塞状态
                locker.wait();
            }
            //队列没有满,可以入队列
            //1.把值放到 tail 的位置
            array[tail] = value;
            tail++;
            //2.判断是否到队尾的情况(等价于 tail = tail % array.length)
            if (tail >= array.length) {
                tail = 0;
            }
            //3.size++
            size++;
            //入队列成功,队列非空,从阻塞队列中唤醒 take
            locker.notify();
        }

    }

    //阻塞式出队列
    public int take() throws InterruptedException {
        synchronized(locker) {
            if (size == 0) {
                //队列为空,没有元素可以出
                //进入阻塞状态
                locker.wait();
            }
            //队列中有元素,可以出队列
            //1.取出 head 位置的值
            int ans = array[head];
            head++;
            //2. 判断是否到队尾
            if (head >= array.length) {
                head = 0;
            }
            //3. size--
            size--;
            //出队列成功,队列不为满,从阻塞队列中唤醒 put
            locker.notify();
            return ans;
        }

    }
}

4. 自己实现生产者消费者模型(代码)

public class Demo2 {
    //自己实现生产者消费者模型
    public static MyBlockingQueue queue = new MyBlockingQueue();
    public static void main(String[] args) {
        Thread producer = new Thread(() -> {
            int num = 0;
            while(true) {
                try {
                    queue.put(num);
                    System.out.println("生产了: " + num);
                    num++;
                    //让生产者生产慢一点,消费者就得跟着生产者的步伐
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        producer.start();

        Thread customer = new Thread(() -> {
           while(true) {
               try {
                   int num = queue.take();
                   System.out.println("消费了: " + num);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
        });
        customer.start();
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Pytorch】 理解张量Tensor

本文参加新星计划人工智能(Pytorch)赛道&#xff1a;https://bbs.csdn.net/topics/613989052 这是目录张量Tensor是什么&#xff1f;张量的创建为什么要用张量Tensor呢&#xff1f;总结张量Tensor是什么&#xff1f; 在深度学习中&#xff0c;我们经常会遇到一个概念&#xff…

更改Hive元数据发生的生产事故

今天同事想在hive里用中文做为分区字段。如果用中文做分区字段的话&#xff0c;就需要更改Hive元 数据库。结果发生了生产事故。导致无法删除表和删除分区。记一下。 修改hive元数据库的编码方式为utf后可以支持中文&#xff0c;执行以下语句&#xff1a; alter table PARTITI…

Vue初入,了解Vue的发展与优缺点

作者简介&#xff1a;一名计算机萌新、前来进行学习VUE,让我们一起进步吧。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;我叫于豆豆吖的主页 前言 从本章开始进行Vue前端的学习&#xff0c;了解Vue的发展&#xff0c;以及背后的故事。 一.vue介…

ASEMI代理瑞萨TW9992AT-NA1-GE汽车芯片

编辑-Z TW9992AT-NA1-GE是一款低功耗NTSC/PAL模拟视频解码器&#xff0c;专为汽车应用而设计。它支持单端、差分和伪差分复合视频输入。集成了对电池短路和对地短路检测&#xff0c;先进的图像增强功能&#xff0c;如可编程的自动对比度调整&#xff08;ACA&#xff09;和MIPI…

【Linux】网络编程套接字(下)

&#x1f387;Linux&#xff1a; 博客主页&#xff1a;一起去看日落吗分享博主的在Linux中学习到的知识和遇到的问题博主的能力有限&#xff0c;出现错误希望大家不吝赐教分享给大家一句我很喜欢的话&#xff1a; 看似不起波澜的日复一日&#xff0c;一定会在某一天让你看见坚持…

ASEMI代理MIMXRT1064CVJ5B原装现货NXP车规级MIMXRT1064CVJ5B

编辑&#xff1a;ll ASEMI代理MIMXRT1064CVJ5B原装现货NXP车规级MIMXRT1064CVJ5B 型号&#xff1a;MIMXRT1064CVJ5B 品牌&#xff1a;NXP /恩智浦 封装&#xff1a;LFGBA-196 批号&#xff1a;2023 安装类型&#xff1a;表面贴装型 引脚数量&#xff1a;196 类型&#…

【Hadoop-yarn-01】大白话讲讲资源调度器YARN,原来这么好理解

YARN作为Hadoop集群的御用调度器&#xff0c;在整个集群的资源管理上立下了汗马功劳。今天我们用大白话聊聊YARN存在意义。 有了机器就有了资源&#xff0c;有了资源就有了调度。举2个很鲜活的场景&#xff1a; 在单台机器上&#xff0c;你开了3个程序&#xff0c;分别是A、B…

Redis知识点汇总

前言 梳理知识 说一下项目中的Redis的应用场景 首先知道Redis的5大value类型: string,list,hash, set ,zset 2.基本上是缓存 3.为的是服务无状态, 4.无锁化 Redis是单线程还是多线程 1.无论什么版本,工作线程就一个 2.6.x高版本出现IO多线程

三天吃透操作系统面试八股文

本文已经收录到Github仓库&#xff0c;该仓库包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等核心知识点&#xff0c;欢迎star~ Github地址&#xff1a;https://github.com/…

基于python的超市历年数据可视化分析

人生苦短 我用python Python其他实用资料:点击此处跳转文末名片获取 数据可视化分析目录人生苦短 我用python一、数据描述1、数据概览二、数据预处理0、导入包和数据1、列名重命名2、提取数据中时间&#xff0c;方便后续分析绘图三、数据可视化1、美国各个地区销售额的分布&…

进阶C语言——指针(二)【题目练习】

文章目录1.指针和数组概念的理解2.指针和数组笔试题解析一维数组字符数组二维数组1.指针和数组概念的理解 指针和数组 数组&#xff1a;能够存放一组相同类型的元素&#xff0c;数组的大小取决于数组的元素个数和元素类型指针&#xff1a;也是地址或指针变量&#xff0c;大小是…

Spring Cloud -- GateWay

为什么需要网关在微服务架构中&#xff0c;一个系统会被拆分为很多个微服务。那么作为客户端要如何去调用这么多的微服务呢&#xff1f;如果没有网关的存在&#xff0c;我们只能在客户端记录每个微服务的地址&#xff0c;然后分别去调用。这样的话会产生很多问题&#xff0c;例…

重构·改善既有代码的设计.04之重构手法(下)完结

1. 前言 本文是代码重构系列的最后一篇啦。前面三篇《重构改善既有代码的设计.01之入门基础》、《重构改善既有代码的设计.02之代码的“坏味道”》、《重构改善既有代码的设计.03之重构手法&#xff08;上&#xff09;》介绍了基础入门&#xff0c;代码异味&#xff0c;还有部…

【Java】你真的懂封装吗?一文读懂封装-----建议收藏

博主简介&#xff1a;努力学习的预备程序媛一枚~博主主页&#xff1a; 是瑶瑶子啦所属专栏: Java岛冒险记【从小白到大佬之路】 前言 write in the front: 如何理解封装&#xff1f; 试想&#xff1a;我们使用微波炉的时候&#xff0c;只用设置好时间&#xff0c;按下“开始”…

[C++]反向迭代器

目录 前言&#xff1a; 1 对反向迭代器的构造思想 2 实现反向迭代器 3 完整代码 前言&#xff1a; 本篇文章主要介绍了STL容器当中的反向迭代器&#xff0c;可能有朋友会说&#xff1a;“反向迭代器有什么好学的&#xff1f;不一样还是迭代器吗&#xff0c;我正向能写出来&…

【js逆向】hook大全

▒ 目录 ▒&#x1f6eb; 导读需求1️⃣ 普通函数2️⃣ 对象方法&#xff08;Class.prototype&#xff09;3️⃣ 对象属性&#xff08;Object.defineProperty&#xff09;4️⃣ Proxy5️⃣ 批量hook示例&#x1f6ec; 文章小结&#x1f4d6; 参考资料&#x1f6eb; 导读 需求 …

【面试题系列】K8S常见面试题

目录 序言 问题 1. 简单说一下k8s集群内外网络如何互通的吧 2.描述一下pod的创建过程 3. 描述一下k8s pod的终止过程 4.Kubernetes 中的自动伸缩有哪些方式&#xff1f; 5.Kubernetes 中的故障检测有哪些方式&#xff1f; 6.Kubernetes 中的资源调度有哪些方式&#xff…

如何优雅的用POI导入Excel文件

在企业级项目开发中&#xff0c;要经常涉及excel文件和程序之间导入导出的业务要求&#xff0c;那么今天来讲一讲excel文件导入的实现。java实现对excel的操作有很多种方式&#xff0c;例如EasyExcel等&#xff0c;今天我们使用的是POI技术实现excel文件的导入。POI技术简介1.P…

全连接神经网络

目录 1.全连接神经网络简介 2.MLP分类模型 2.1 数据准备与探索 2.2 搭建网络并可视化 2.3 使用未预处理的数据训练模型 2.4 使用预处理后的数据进行模型训练 3. MLP回归模型 3.1 数据准备 3.2 搭建回归预测网络 1.全连接神经网络简介 全连接神经网络(Multi-Layer Percep…

基于Vue3和element-plus实现一个完整的登录功能

先看一下最终要实现的效果:登录页面:注册页面:(1)引入element-plus组件库引入组件库的方式有好多种,在这里我就在main.js全局引入了.npm i element-plus -Smain.js中代码:import { createApp } from "vue"; //element-plus import ElementPlus from "element-pl…