多线程JUC:等待唤醒机制(生产者消费者模式)

news2024/11/19 8:38:51

👨‍🎓作者简介:一位大四、研0学生,正在努力准备大四暑假的实习
🌌上期文章:多线程&JUC:解决线程安全问题——synchronized同步代码块、Lock锁
📚订阅专栏:多线程&JUC
希望文章对你们有所帮助

等待唤醒机制(生产者消费者模式)

  • 等待唤醒机制
  • 等待唤醒机制的实现
    • 消费者代码实现
    • 生产者代码实现
  • 阻塞队列实现等待唤醒机制

等待唤醒机制

等待唤醒机制也叫做生产者消费者模式,打破了以前线程间执行的随机性,生产者消费者模式能够使得线程之间是轮流运行的。是一个非常经典的多线程协作的模式。
对于两条线程,其中一条为生产者,另一条为消费者,大家都是学习过操作系统的,原理多少还是记得一些的。

对于等待唤醒机制,其只有2种情况:

1、消费者等待:若没有可以被消费者消费的数据,那么消费者就是进入wait状态,这时候生产者就可以抢占CPU生产数据,接着notify(唤醒)消费者
2、生产者等待:若已经有数据供给消费者消费,则生产者进入wait状态,消费者抢占CPU消费数据,接着notify(唤醒)生产者

在这其中可能会涉及到的方法:

方法名称说明
void wait()当前线程等待,直到被其他线程唤醒
void notify()随机唤醒单个线程
void notifyAll()唤醒所有线程

等待唤醒机制的实现

消费者代码实现

消费者和生产者中间有一个控制他们执行相应操作的核心,视为Controller,记录一些状态变量和锁对象:

public class Controller {
    /**
     * 控制消费者和生产者的执行
     */
    //表示是否有数据 0:没有 1:有
    public static int flag = 0;

    //消费者最多可以消费的数据量
    public static int count = 10;

    //锁对象
    public static Object lock = new Object();
}

接着实现消费者的逻辑:

public class Consumer extends Thread{
    @Override
    public void run() {
        while(true){
            synchronized (Controller.lock) {
                if(Controller.count == 0){
                    //消费者已经消费量了10次,退出
                    break;
                }else{
                    //先判断有无可以消费的数据
                    if(Controller.flag == 0) {
                        //若无,等待
                        //用lock调用wait方法,使得当前线程与锁进行绑定,之后唤醒就唤醒这些被绑定了的线程
                        try {
                            Controller.lock.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }else{
                        //若有,消费
                        System.out.println("正在消费,还可以消费" + --Controller.count + "个");
                        //消费完后唤醒生产者,唤醒绑定在这把锁上的所有线程
                        Controller.lock.notifyAll();
                        //修改控制中心的状态
                        Controller.flag = 0;
                    }
                }
            }
        }
    }
}

生产者代码实现

public class Producer extends Thread{
    @Override
    public void run() {
        while (true){
            synchronized (Controller.lock){
                if(Controller.count == 0){
                    break;
                }else{
                    if(Controller.flag == 1){
                        //已经有供给消费者进行消费的数据
                        try {
                            Controller.lock.wait();
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }else{
                        System.out.println("成功生产");
                        Controller.lock.notifyAll();
                        Controller.flag = 1;
                    }
                }
            }
        }
    }
}

最后编写测试类代码验证:

public class ThreadDemo {
    public static void main(String[] args) {
        //创建线程对象
        Producer producer = new Producer();
        Consumer consumer = new Consumer();
        //给线程设置名字
        producer.setName("生产者");
        consumer.setName("消费者");
        //开启线程
        producer.start();
        consumer.start();
    }
}

阻塞队列实现等待唤醒机制

何为阻塞队列?其实就是连接生产者和消费者的一个队列,管理着数据,分别供消费者take和生产者的put,如果put不进去或者take不出,则说明队列满了或者空了,这时候就会进入阻塞状态。

阻塞队列BlockingQueue本身实现了Iterable、Collection、Queue的接口,无法直接实例化,但是其具有2个实现类:

1、ArrayBlockingQueue:底层为数组,有界
2、LinkedBlockingQueue:底层为链表,无界(不是真正的无界,最大为int的最大范围,只是无须指定范围)

利用阻塞队列来实现是很便捷的,因为我们可以查看put和take方法的底层,可以发现这两个方法是自带锁的,所以我们在实现生产者和消费者的时候无须自己上锁,否则反而会容易因为锁的嵌套而发生死锁。
在这里插入图片描述
在这里插入图片描述
生产者代码:

public class Producer extends Thread{

    ArrayBlockingQueue<String> queue;

    public Producer(ArrayBlockingQueue<String> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        while (true) {
            //直接不断的把数据放进阻塞队列,如果满了它自己会阻塞
            try {
                queue.put("数据");
                System.out.println("消费者生产了一个数据到阻塞队列");
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

消费者代码:

public class Consumer extends Thread{

    ArrayBlockingQueue<String> queue;

    public Consumer(ArrayBlockingQueue<String> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        while (true){
            try {
                String take = queue.take();
                System.out.println(take);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

测试类:

public class ThreadDemo {
    /**
     * 使用阻塞队列实现等待唤醒机制,要保证生产者和消费者用的是同一个阻塞队列
     */
    public static void main(String[] args) {
        //创建一个可以存放1个数据的阻塞队列
        ArrayBlockingQueue<String> queue = new ArrayBlockingQueue<>(1);
        //创建生产者和消费者对象,并把阻塞队列传递过去,使得它们使用同一个阻塞队列
        Producer producer = new Producer(queue);
        Consumer consumer = new Consumer(queue);

        producer.setName("生产者");
        consumer.setName("消费者");

        producer.start();
        consumer.start();
    }
}

在这里插入图片描述
最后显示可能会重复打印数据,这是因为输出的语句没有放在锁里面,锁可以执行的put和take已经写死了,但是并不影响我们实际数据的并发安全性,只是不方便我们的观察罢了。

至此,阻塞队列实现等待唤醒机制的demo已经跑通了,阻塞队列底层的执行实际上是异步的,可以解决在实际生产环境中的超卖问题,具体可以看我之前的文章:
Redis:原理速成+项目实战——Redis实战9(秒杀优化)

当然,主流的方法还是使用消息队列RabbitMQ或Kafka,这个大家可以自行去了解。

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

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

相关文章

卫星通讯领域FPGA关注技术:算法和图像方面(2)

最近关注的公众号提到了从事移动通信、卫星通讯等领域的FPGA、ASIC、信号处理算法等工程师可能需要关注的技术&#xff0c;有MVDR算法、高速基带芯片、RF芯片、毫米波有源相控阵天线、无线AI&#xff0c;以下做了一些基础的调研&#xff1a; 1 MVDR算法 声源定位是一个阵列信…

【数据结构与算法】【约瑟夫问题】还在用递归?教你用链表秒杀约瑟夫

&#x1f389;&#x1f389;欢迎光临&#x1f389;&#x1f389; &#x1f3c5;我是苏泽&#xff0c;一位对技术充满热情的探索者和分享者。&#x1f680;&#x1f680; &#x1f31f;特别推荐给大家我的最新专栏《数据结构与算法&#xff1a;初学者入门指南》&#x1f4d8;&am…

【Kubernetes】在k8s1.24及以上版本基于containerd容器运行时测试pod从harbor拉取镜像

基于containerd容器运行时测试pod从harbor拉取镜像 1、安装高版本containerd2、安装docker3、登录harbor上传镜像4、从harbor拉取镜像 1、安装高版本containerd 集群中各个节点都要操作 yum remove containerd.io -y yum install containerd.io-1.6.22* -y cd /etc/containe…

融资项目——获取树形结构的数据

如下图所示&#xff0c;下列数据是一个树形结构数据&#xff0c;行业中包含若干子节点。表的设计如下图&#xff0c;设置了一个id为1的虚拟根节点。&#xff08;本树形结构带虚拟根节点共三层&#xff09; 实现逻辑&#xff1a; 延时展示方法&#xff0c;先展现第二层的信息&a…

动态内存经典笔试题分析

1.代码1 void GetMemory(char *p) { p (char *)malloc(100); } void Test(void) { char *str NULL; GetMemory(str); strcpy(str, "hello world"); printf(str); } int main&#xff08;&#xff09; { Test&#xff08;&#xff09;&#xff1b; return 0&#x…

RocketMQ生产常见问题

RocketMQ如何保证消息不丢失 1、哪些环节会有丢消息的可能&#xff1f; 其中&#xff0c;1&#xff0c;2&#xff0c;4三个场景都是跨网络的&#xff0c;而跨网络就肯定会有丢消息的可能。关于3这个环节&#xff0c;通常MQ存盘时都会先写入操作系统的缓存page cache中&#xf…

【MySQL】字符串函数的学习

&#x1f308;个人主页: Aileen_0v0 &#x1f525;热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​&#x1f4ab;个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-J7VN4RbrBi51ozap {font-family:"trebuchet ms",verdana,arial,sans-serif;font-siz…

【洛谷题解】B2056 求整数的和与均值

题目链接&#xff1a;求整数的和与均值 - 洛谷 题目难度&#xff1a;入门 涉及知识点&#xff1a;求和&#xff0c;平均值 题意&#xff1a; 输入样例&#xff1a; 4 344 222 343 222 输出样例&#xff1a; 1131 282.75000 分析&#xff1a;直接累加&#xff0c;再求平…

【知识整理】招人理念、组织结构、招聘

1、个人思考 几个方面&#xff1a; 新人&#xff1a;选、育、用、留 老人&#xff1a;如何甄别&#xff1f; 团队怎么演进&#xff1f; 有没有什么注意事项 怎么做招聘&#xff1f; 2、 他人考虑 重点&#xff1a; 1、从零开始&#xff0c;讲一个搭建团队的流程 2、标…

大数据应用对企业的价值

目录 一、大数据应用价值 1.1 大数据技术分析 1.2 原有技术场景的优化 1.2.1 数据分析优化 1.2.2 高并发数据处理 1.3 通过大数据构建新需求 1.3.1 智能推荐 1.3.2 广告系统 1.3.3 产品/流程优化 1.3.4 异常检测 1.3.5 智能管理 1.3.6 人工智能和机器学习 二、大数…

android中使用Bitmp对象绘制图形

1、引言 你是否还在因为不懂UI设计而不得不去借用别人的图片&#xff0c;甚至使用各种网图作为界面布局的一部分&#xff0c;那么今天就教你使用Bitmap对象去绘制自定义图形&#xff0c;并保存为png格式的图片&#xff0c;须知图片编辑软件本就是程序员开发出来的&#xff0c;我…

fast.ai 机器学习笔记(四)

机器学习 1&#xff1a;第 11 课 原文&#xff1a;medium.com/hiromi_suenaga/machine-learning-1-lesson-11-7564c3c18bbb 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 来自机器学习课程的个人笔记。随着我继续复习课程以“真正”理解它&#xff0c;这些笔记将继续…

Frostmourne (霜之哀伤)日志告警系统部署安装

简介 Frostmourne(霜之哀伤)是汽车之家经销商技术部监控系统的开源版本&#xff0c;用于帮助监控几乎所有数据库数据(包括Elasticsearch, Prometheus, SkyWalking, MySql 等等)。如果你已经建立起了日志系统&#xff0c; 指标体系&#xff0c;却苦恼于没有一个配套监控系统&am…

【第二十三课】最小生成树:prime 和 kruskal 算法(acwing858,859 / c++代码 )

目录 前言 Prime算法--加点法 acwing-858 代码如下 一些解释 Kruskal算法--加边法 acwing-859 并查集与克鲁斯卡尔求最小生成树 代码如下 一些解释 前言 之前学最短路的时候&#xff0c;我们都是以有向图为基础的&#xff0c;当时我们提到如果是无向图&#xf…

二、Mybatis相关概念

1.对象/关系数据库映射&#xff08;ORM) ORM全称Object/Relation Mapping&#xff1a;表示对象-关系映射的缩写ORM完成面向对象的编程语言到关系数据库的映射。当ORM框架完成映射后&#xff0c;程序员既可以利用面向对象程序设计语言的简单易用性&#xff0c;又可以利用关系数…

Swift 隐藏宝藏:“逆天改命”调整方法重载(function overloading)优先级

概览 在 Swift 语言中有很多隐藏“宝藏”悄悄深埋在不为人知的角落&#xff0c;静静等待着有缘秃头码农们的大力挖掘。 而在这里&#xff0c;我们将介绍 Swift 语言中一个非常有用的秘技&#xff1a;方法重载优先级判断以及如何改变它。 在本篇博文中&#xff0c;您将学到如下…

巴尔加瓦算法图解:算法运用(上)

目录 树反向索引傅立叶变换 并行算法MapReduce函数 树 如果能将用户名插入到数组的正确位置就好了&#xff0c;这样就无需在插入后再排序。为此&#xff0c;有人设计了一种名为二叉查找树(binary search tree)的数据结构。 每个node的children 都不大于两个。对于其中的每个…

7点原因,解密可视化大屏为啥要3D效果

真实感&#xff1a; 3D效果可以让用户感受到更真实的视觉效果&#xff0c;让用户更加身临其境&#xff0c;增强用户的参与感和沉浸感。 立体感&#xff1a; 3D效果可以给用户带来更强的立体感&#xff0c;让用户更加容易理解和感知物体的形状和大小。 美观性&#xff1a; 3D…

Android13多媒体框架概览

Android13多媒体框架概览 Android 多媒体框架 Android 多媒体框架旨在为 Java 服务提供可靠的接口。它是一个系统&#xff0c;包括多媒体应用程序、框架、OpenCore 引擎、音频/视频/输入的硬件设备&#xff0c;输出设备以及一些核心动态库&#xff0c;比如 libmedia、libmedi…

【OrangePi Zero2 智能家居】阿里云人脸识别方案

一、接入阿里云 二、C语言调用阿里云人脸识别接口 三、System V消息队列和POSIX 消息队列 一、接入阿里云 在之前树莓派的人脸识别方案采用了翔云平台的方案去1V1上传比对两张人脸比对&#xff0c;这种方案是可行&#xff0c;可 以继续采用。但为了接触更多了云平台方案&…