JavaEE(系列9) -- 多线程案列2(堵塞队列)

news2024/10/6 16:16:28

目录

1. 堵塞队列

2. 生产者消费者模型

2.1 解耦合 

2.2 削峰填谷

2.3 代码实现生产者消费者模型

3. 构建堵塞队列

3.1 实现普通队列(循环队列) 

3.2 普通队列加上线程安全

3.3 普通队列实现堵塞功能

3.4 堵塞队列最终代码

4. 使用生产者消费者模型测试自己构建的堵塞队列 


1. 堵塞队列

阻塞队列是一种特殊的队列. 也遵守 "先进先出" 的原则

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

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

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

代码演示

 

2. 生产者消费者模型

  1. 生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题
  2. 生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取.

 生产者消费者模型生活举例(包饺子)

 那么生产者消费者模型主要解决是什么呢?

1. 解耦合

2. 削峰填谷

2.1 解耦合 

在未来我们编写代码的时候,我们写的代码一定要尽量满足"高内聚,低耦合"这样的形式. 

低耦合:两个模块之间的关联程度越高,耦合就越高,我们写代码要做到低耦合,防止牵一发动全身的情况

高内聚:相关联的代码,要规整的放在一起,避免想用一段代码的时候出现找不到或者找错的行为.

那么堵塞队列怎么实现的解耦合呢?

举例说明

 如果引入生产者消费者模型,就能解耦合.

2.2 削峰填谷

2.3 代码实现生产者消费者模型

package blockingQueue;

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * Created with IntelliJ IDEA.
 * Description:基于堵塞队列构建一个生产者消费者模型
 * User: YAO
 * Date: 2023-05-16
 * Time: 16:40
 */
public class ProducerConsumerModel {
    public static void main(String[] args) {

        BlockingQueue<Integer> queue = new LinkedBlockingQueue<>();

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

        t1.start();

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

        t2.start();

        //上述代码,让生产者,每隔1秒,生产一个元素
        // 消费者不受限制
    }
}

运行结果:

 

 我们生活中的例子:三峡大坝

3. 构建堵塞队列

自己实现堵塞队列主要有三步:

  1. 实现一个普通队列
  2. 加上线程安全
  3. 加上阻塞功能

3.1 实现普通队列(循环队列) 

public class MyBlockingQueue {
    private int[] items = new int[100];

    // 约定[head, tail)队列  循环队列
    private int head;
    private int tail;
    private int size;

    public void put(int elem){
        if (isFull()){
           return;
        }
        items[tail] = elem;
        tail++;

        if (tail == items.length){
            tail = 0;
        }
        // 取余数 tail = tail % items.length;  但是并不推荐(可读行不好),执行效率(低)
        size++;
    }
    public Integer take(){
        if (isEmpty()){
            return null;
        }
        int value = items[head];
        head++;
        if (head == items.length){
            head = 0;
        }
        size--;
        return value;
    }
    public boolean isFull(){
        return size == items.length;
    }
    public boolean isEmpty(){
        return size == 0;
    }
}

3.2 普通队列加上线程安全

观察上述代码,我们想加入多线程,当我们往队列里面加入元素,和往外面取出元素的这个过程,都要判断是否为空(满),并且此过程对变量有读写的过程,那么我们就应该将我们的成员变量,使用volatile进行修饰,防止出现指令重排序.

3.3 普通队列实现堵塞功能

1.当我们判断队列满的时候,再往队列添加元素的时候,我们不返回,而等待队列出元素的时候,进入堵塞的状态,等待接收通知,再往队列添加元素.

2.同理:当我们判断队列为空的时候,要从对列取出元素的时候,我们不进行返回null,同样进入堵塞的状态,等待入队的通知,在从队列中取元素

实现以上操作,就可以使用wait notify

 上述代码就是对于入队列和出队列使用wait notify 实现堵塞功能.

思考:上述代码,是否还是存在弊端?

答案:是的,很有可能再别的代码中使用interrupt给wait唤醒,那么我们就需要再次进行判断是否符合唤醒的条件,也就是将判断队列是否为空(满)写成一个while循环.

  

3.4 堵塞队列最终代码

package blockingQueue;

/**
 * Created with IntelliJ IDEA.
 * Description:针对wait部分使用while优化
 * User: YAO
 * Date: 2023-05-17
 * Time: 9:49
 */
public class MyBlockingQueue2 {
    private int[] items = new int[100];

    // 约定[head, tail)队列  循环队列
    volatile private int head;
    // 给所有的读操作对应的变量使用volatile关键字进行修饰
    volatile private int tail;
    volatile private int size;

    synchronized public  void put(int elem) throws InterruptedException {
        // 针对this加锁
        while (isFull()){
            //return;
            this.wait();
            //如果队列满了,就进入阻塞,等待出队列的notify进行唤醒
            //注意:这样使用wait()是不太正确的,很有可能在别的代码暗中interrupt,把wait唤醒了
            //明明条件还没满足条件,但是把wait()给唤醒了,此时就会在这种情况导致栈溢出

            //进行优化,wait()被唤醒之后,在确认一下条件是不是满足,如果还不满足就继续wait(),就使用while循环
        }
        items[tail] = elem;
        tail++;

        if (tail == items.length){
            tail = 0;
        }
        // 取余数 tail = tail % items.length;  但是并不推荐(可读行不好),执行效率(低)
        size++;
        this.notify();
        // 此时往空队列加入了元素,用来唤醒因为队列为空,而进入堵塞的出队列操作
    }
    synchronized public Integer take() throws InterruptedException {
        while (isEmpty()){
            //return null;
            this.wait();
            //如果队列为空,就进入阻塞,等待入队列的notify进行唤醒
        }
        int value = items[head];
        head++;
        if (head == items.length){
            head = 0;
        }
        size--;
        this.notify();
        // 此时满队列出了元素,用来唤醒因为队列满,而进入堵塞的入队列操作
        return value;
    }
    public boolean isFull(){
        return size == items.length;
    }
    public boolean isEmpty(){
        return size == 0;
    }
}

4. 使用生产者消费者模型测试自己构建的堵塞队列 

package blockingQueue;

/**
 * Created with IntelliJ IDEA.
 * Description:基于自己构建的堵塞队列构建一个生产者消费者模型
 * User: YAO
 * Date: 2023-05-17
 * Time: 9:32
 */
public class MyProducerConsumerModel {
    public static void main(String[] args) {
        MyBlockingQueue queue = new MyBlockingQueue();

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

        t1.start();

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

        });
        t2.start();

        System.out.println(" ");
    }
}

运行结果:

 根据我们的设置,生产者瞬间生产了100个元素,进入堵塞,然后设置消费者,每隔1秒消费一个元素,生产者再继续生产.

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

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

相关文章

IP协议的相关特性、数据链路层相关内容讲解

文章目录 IP协议相关特性地址管理NAT机制IP地址的组成特殊的IP地址 路由选择 数据链路层相关内容以太网MTU IP协议相关特性 首先我们来认识一下IP协议的报头&#xff1a; 4位版本号&#xff1a;指定IP协议的版本&#xff0c;对于IPv4就是4. 4位头部长度&#xff1a;IP头部的长…

每日一题158—— 图片平滑器

图像平滑器 是大小为 3 x 3 的过滤器&#xff0c;用于对图像的每个单元格平滑处理&#xff0c;平滑处理后单元格的值为该单元格的平均灰度。 每个单元格的 平均灰度 定义为&#xff1a;该单元格自身及其周围的 8 个单元格的平均值&#xff0c;结果需向下取整。&#xff08;即…

常用中外文献检索网站大盘点

一、常用中文文献检索权威网站&#xff1a; 1、知网&#xff1a;是全球最大的中文数据库。提供中国学术文献、外文文献、学位论文、报纸、会议、年鉴、工具书等各类资源&#xff0c;并提供在线阅读和下载服务。涵盖领域包括&#xff1a;基础科学、文史哲、工程科技、社会科学、…

世界博物馆日:一起来看看这些“不太正经”的文物!

今天是5月18日&#xff0c;世界博物馆日。 这两年喜欢逛博物馆的年轻人越来越多了。和爬山、露营一样&#xff0c;博物馆打卡已经是这一届年轻人最受欢迎的娱乐方式之一了。 今天我们要和大家分享的是&#xff1a;全国各地博物馆里收藏的那些萌的、凶的、神秘的、搞笑的…精品…

从零开始 Spring Boot 31:Spring 表达式语言

从零开始 Spring Boot 31&#xff1a;Spring 表达式语言 图源&#xff1a;简书 (jianshu.com) Spring表达式语言&#xff08;Spring Expression Language&#xff0c;简称 “SpEL”&#xff09;是一种强大的表达式语言&#xff0c;支持在运行时查询和操作对象图。该语言的语法…

2024王道数据结构考研丨第一章:绪论

2024王道数据结构考研笔记专栏将持续更新&#xff0c;欢迎 点此 收藏&#xff0c;共同交流学习… 文章目录 第一章&#xff1a;绪论1.1数据结构的基本概念1.2数据结构的三要素1.3算法的基本概念1.4算法的时间复杂度1.5算法的空间复杂度 第一章&#xff1a;绪论 1.1数据结构的基…

Vue - vxe-table 表格合并行应用

Vue - vxe-table 表格合并行应用 一. 将相同的列数据合并为一行实现效果实现方法 二. 拓展合并&#xff1a;根据某个字段合并后的数据 进行合并其他字段列实现效果实现方法 vxe-table 地址&#xff1a;https://vxetable.cn/v2/#/table/start/install 一. 将相同的列数据合并为一…

亚马逊云科技作为中国出海力量之一,为中国企业提供技术桥梁

这是一个真实的故事&#xff1a;一家出海企业的项目交付需要在非洲吉布提部署上云&#xff0c;企业负责人在地图上找了半天才找到吉布提&#xff0c;而亚马逊云科技仅用了3天的时间就为企业在当地的业务开展&#xff0c;交付了IT基础设施。对于出海企业来说&#xff0c;这种效率…

前端学习--Vue(2)常见指令

一、Vue简介 1.1 概念 Vue是一套用于构建用户界面的前端框架 框架&#xff1a;现成解决方案&#xff0c;遵守规范去编写业务功能 指令、组件、路由、Vuex、vue组件库 1.2 特性 数据驱动视图 vue连接页面结构和数据&#xff0c;监听数据变化&#xff0c;自动渲染页面结构…

【遗传算法】【处理图像类问题】

文章目录 一、前言二、问题描述三、算法介绍四、其他知识点Reference 一、前言 近期感兴趣的算法&#xff0c;以前没这么好奇过一个算法。时间没想象的焦虑&#xff0c;认真做一些事情算法入门篇 二、问题描述 从前&#xff0c;一群扇贝在海岸边悠哉游哉地生活着。它们衣食不…

亚马逊云科技宣布推出一个新的开源示例应用程序

5月5日&#xff0c;亚马逊云科技宣布推出一个新的开源示例应用程序&#xff0c;这是一个虚构的二手书电子商务商店&#xff0c;被称之为Bob’s Used Books&#xff0c;可供使用亚马逊云科技的.NET开发人员使用。“亚马逊云科技的.NET宣传和开发团队定期与客户交谈&#xff0c;在…

如何科学地利用高光谱图像合成真彩色RGB图像?

如何科学地利用高光谱图像合成真彩色RGB图像? 1. 前言 参考链接: 色匹配函数是什么&#xff1f; - 知乎 (zhihu.com) 23. 颜色知识1-人类的视觉系统与颜色 - 知乎 (zhihu.com) 色彩空间基础 - 知乎 (zhihu.com) 色彩空间表示与转换 - 知乎 (zhihu.com) CIE XYZ - fresh…

Golang笔记:使用melody包进行WebSocket通讯

文章目录 目的使用示例与说明总结 目的 WebSocket是Web开发应用中非常常用的功能&#xff0c;用于客户端和服务器间长时间的实时双向数据通讯。Golang中官方并没有实现这个功能&#xff0c;需要借助第三方的包来实现。 目前被最广泛使用的包是 gorilla/websocket https://pkg…

Host头攻击

转载与&#xff1a;https://blog.csdn.net/weixin_47723270/article/details/129472716 01 HOST头部攻击漏洞知识 Host首部字段是HTTP/1.1新增的&#xff0c;旨在告诉服务器&#xff0c;客户端请求的主机名和端口号&#xff0c;主要用来实现虚拟主机技术。 运用虚拟主机技术&a…

第八章:C语言的简单指针

谈起指针&#xff0c;简直就是谈虎色变&#xff0c;学习C语言的人都知道&#xff0c;指针的难度&#xff0c;就好像高中的导数一样&#xff0c;难道离谱&#xff0c;但是但是&#xff0c;别慌呀&#xff0c;咱们慢慢来&#xff0c;空杯心态&#xff0c;一步一个脚印&#xff0c…

【STM32】STM32使用继电器

STM32使用继电器 其实继电器简单来说就是一个开关&#xff0c;VCC表示电源正极、GND表示电源负极、IN表示信号输入脚&#xff0c;COM表示公共端&#xff0c;NC&#xff08;normal close&#xff09;表示常闭端&#xff0c;NO(normal open)表示常开端。一般情况下是常闭状态。 …

为数字人充值AI情商 小冰“克隆人”要卖给谁?

近日&#xff0c;小冰公司启动“GPT克隆人计划”&#xff0c;据悉最短只要采集三分钟数据&#xff0c;就能帮助用户创造源于本人性格、技能、声音、外貌的AI克隆人&#xff0c;如同拥有“平行世界的第二人生”。 这不免让人想起了《流浪地球2》里华仔为剧中女儿“数字续命”的…

chatgpt赋能Python-python3_2__1

Python3-2<<1&#xff1a; 了解运算符的使用和优先级 Python是一种优雅而高效的编程语言&#xff0c;而Python3-2<<1是一个关于运算符优先级的例子&#xff0c;值得我们深入探讨。 在这篇文章中&#xff0c;我们将介绍Python3中运算符的优先级&#xff0c;并对其中…

chatgpt赋能Python-python3_6怎么用

Python3.6是什么&#xff1f; Python是一种非常流行的编程语言&#xff0c;旨在提供简洁、易读且易于维护的代码。Python3.6是该语言的下一个主要版本&#xff0c;带来了一些改进并改进了现有功能&#xff0c; Python3.6有哪些新特性&#xff1f; 字典内置方法&#xff0c;支…

chatgpt赋能Python-python3_53怎么安装

Python3.5.3安装方法及注意事项 Python是一种高级编程语言&#xff0c;被广泛应用于科学&#xff0c;数学&#xff0c;机器学习等领域。在本文中&#xff0c;我们将介绍如何安装Python 3.5.3版本&#xff0c;并提供注意事项以确保安装过程顺利进行。 步骤1&#xff1a;下载Py…