多线程JUC:等待唤醒机制(生产者消费者模式)
👨🎓作者简介:一位大四、研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{@Overridepublic 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{@Overridepublic 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;}@Overridepublic 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;}@Overridepublic 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,这个大家可以自行去了解。
相关文章:

多线程JUC:等待唤醒机制(生产者消费者模式)
👨🎓作者简介:一位大四、研0学生,正在努力准备大四暑假的实习 🌌上期文章:多线程&JUC:解决线程安全问题——synchronized同步代码块、Lock锁 📚订阅专栏:多线程&am…...

无人机动力系统高倍率锂聚合物电池介绍,无人机锂电池使用与保养,无人机飞行控制动力源详解
无人机电池使用及保养 电池是无人机飞行的动力来源,也是一个消耗品,对电池充分了解,采取正确的使用方法,妥善进行维护保养将有助于提高飞行的安全性、延长电池的使用寿命。以下将详细对电池的使用和管理进行讲解。 高倍率锂聚合物电池的含义…...

[BeginCTF]真龙之力
安装程序 双击安装 出现了安装失败的标签,开发者不允许测试。 查看Mainfest入口文件 <?xml version"1.0" encoding"utf-8"?> <manifest xmlns:android"http://schemas.android.com/apk/res/android" android:versionCo…...

手写分布式存储系统v0.3版本
引言 承接 手写分布式存储系统v0.2版本 ,今天开始新的迭代开发。主要实现 服务发现功能 一、什么是服务发现 由于咱们的服务是分布式的,那从服务管理的角度来看肯定是要有一个机制来知道具体都有哪些实例可以提供服务。举个例子就是,张三家…...
除夕快乐!
打印的简单实现,祝大家新的一年万事顺意! 龙年大吉! #include <stdio.h> #include <windows.h> #include <string.h>int main() {const char* message "除夕快乐!";int i;for (i 0; i < strlen(message);…...

17:定时器编程实战
1、实验目的 (1)使用定时器来完成LED闪烁 (2)原来实现闪烁时中间的延迟是用delay函数实现的,在delay的过程中CPU要一直耗在这里不能去做别的事情。这是之前的缺点 (3)本节用定时器来定一个时间(譬如0.3s),在这个定时器定时时间内…...

Fink CDC数据同步(五)Kafka数据同步Hive
6、Kafka同步到Hive 6.1 建映射表 通过flink sql client 建Kafka topic的映射表 CREATE TABLE kafka_user_topic(id int,name string,birth string,gender string ) WITH (connector kafka,topic flink-cdc-user,properties.bootstrap.servers 192.168.0.4:6668…...

ubuntu原始套接字多线程负载均衡
原始套接字多线程负载均衡是一种在网络编程中常见的技术,特别是在高性能网络应用或网络安全工具中。这种技术允许应用程序在多个线程之间有效地分配和处理网络流量,提高系统的并发性能。以下是关于原始套接字多线程负载均衡技术的一些介绍: …...

leetcode (算法)66.加一(python版)
需求 给定一个由 整数 组成的 非空 数组所表示的非负整数,在该数的基础上加一。 最高位数字存放在数组的首位, 数组中每个元素只存储单个数字。 你可以假设除了整数 0 之外,这个整数不会以零开头。 示例 1: 输入:digi…...
DataX源码分析 TaskGroupContainer
系列文章目录 一、DataX详解和架构介绍 二、DataX源码分析 JobContainer 三、DataX源码分析 TaskGroupContainer 四、DataX源码分析 TaskExecutor 五、DataX源码分析 reader 六、DataX源码分析 writer 七、DataX源码分析 Channel 文章目录 系列文章目录TaskGroupContainer初始…...
2024年华为OD机试真题-螺旋数字矩阵-Java-OD统一考试(C卷)
题目描述: 疫情期间,小明隔离在家,百无聊赖,在纸上写数字玩。他发明了一种写法: 给出数字个数n和行数m(0 < n ≤ 999,0 < m ≤ 999),从左上角的1开始,按照顺时针螺旋向内写方式,依次写出2,3...n,最终形成一个m行矩阵。 小明对这个矩阵有些要求: 1.每行数字的…...

红队打靶练习:PHOTOGRAPHER: 1
目录 信息收集 1、arp 2、nmap 3、nikto 目录扫描 1、gobuster 2、dirsearch WEB 信息收集 enum4linux smbclient 8000端口 CMS利用 信息收集 文件上传漏洞利用 提权 信息收集 get user.txt get flag 信息收集 1、arp ┌──(root㉿ru)-[~/kali] └─# a…...

【Linux】网络诊断 traceroute命令详解
目录 一、traceroute概述 1.1 traceroute命令简介 1.2 命令格式 1.3 原理 1.4 命令功能 二、使用实例 实例1:traceroute 用法简单、最常用的用法 实例2:跳数设置 实例3:设置探测数据包数量 实例4:显示IP地址,…...

c#cad 创建-圆(二)
运行环境 vs2022 c# cad2016 调试成功 一、代码说明 这段代码是一个AutoCAD插件,用于在模型空间中创建一个圆形。 首先,我们需要定义一个命令类CreateCircleCommand,并在命名空间CreateCircleInCad中声明。 在CreateCircleCommand类中&a…...
面试高频知识点:2线程 2.1.5如何自定义实现一个线程池
在Java中,线程池是一种用于管理线程的机制,它可以有效地管理多个线程并且可以重复使用它们,从而减少了线程创建和销毁的开销,提高了线程的利用率。本文将介绍如何自定义实现一个简单的线程池,并提供相应的Java代码示例…...

【stm32】hal库学习笔记-ADC模数转换(超详细)
【stm32】hal库学习笔记-ADC模数转换(超详细) 本篇章介绍了ADC实现电压检测的三种方式 ADC原理及选型 ADC将连续的模拟电压信号转换为二进制的数字信号 选型参数 速度(采样频率) 功耗 精度 转换原理 ADC hal库驱动函数 普通…...
蓝桥杯基础知识6 pair
蓝桥杯基础知识6 pair pair 的定义和结构:在C中,pair是一个模板类,用于表示一对值的组合,头文件<utility>。 pair类 的定义: template<class T1, class T2> struct pair{T1 first; // 第一个值T2 seco…...
后端返回给前端的数据格式有哪些?
后端返回的数据格式有很多种,常见的包括JSON、XML、HTML、CSV等。这些格式各有特点,适用于不同的应用场景。 JSON(JavaScript Object Notation):JSON是一种轻量级的数据交换格式,易于阅读和编写,…...

Transformer的PyTorch实现之若干问题探讨(一)
《Transformer的PyTorch实现》这篇博文以一个机器翻译任务非常优雅简介的阐述了Transformer结构。在阅读时存在一些小困惑,此处权当一个记录。 1.自定义数据中enc_input、dec_input及dec_output的区别 博文中给出了两对德语翻译成英语的例子: # S: de…...
系统参数SystemParameters.MinimumHorizontalDragDistance
SystemParameters.MinimumHorizontalDragDistance 是一个系统参数,它表示在拖放操作中鼠标水平移动的最小距离。 当用户按下鼠标左键并开始移动鼠标时,系统会检查鼠标的水平移动距离是否超过了 SystemParameters.MinimumHorizontalDragDistance。只有当…...
Python|GIF 解析与构建(5):手搓截屏和帧率控制
目录 Python|GIF 解析与构建(5):手搓截屏和帧率控制 一、引言 二、技术实现:手搓截屏模块 2.1 核心原理 2.2 代码解析:ScreenshotData类 2.2.1 截图函数:capture_screen 三、技术实现&…...
Android Wi-Fi 连接失败日志分析
1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分: 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析: CTR…...

解决Ubuntu22.04 VMware失败的问题 ubuntu入门之二十八
现象1 打开VMware失败 Ubuntu升级之后打开VMware上报需要安装vmmon和vmnet,点击确认后如下提示 最终上报fail 解决方法 内核升级导致,需要在新内核下重新下载编译安装 查看版本 $ vmware -v VMware Workstation 17.5.1 build-23298084$ lsb_release…...

对WWDC 2025 Keynote 内容的预测
借助我们以往对苹果公司发展路径的深入研究经验,以及大语言模型的分析能力,我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际,我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测,聊作存档。等到明…...

如何将联系人从 iPhone 转移到 Android
从 iPhone 换到 Android 手机时,你可能需要保留重要的数据,例如通讯录。好在,将通讯录从 iPhone 转移到 Android 手机非常简单,你可以从本文中学习 6 种可靠的方法,确保随时保持连接,不错过任何信息。 第 1…...

2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...
Caliper 配置文件解析:config.yaml
Caliper 是一个区块链性能基准测试工具,用于评估不同区块链平台的性能。下面我将详细解释你提供的 fisco-bcos.json 文件结构,并说明它与 config.yaml 文件的关系。 fisco-bcos.json 文件解析 这个文件是针对 FISCO-BCOS 区块链网络的 Caliper 配置文件,主要包含以下几个部…...

蓝桥杯3498 01串的熵
问题描述 对于一个长度为 23333333的 01 串, 如果其信息熵为 11625907.5798, 且 0 出现次数比 1 少, 那么这个 01 串中 0 出现了多少次? #include<iostream> #include<cmath> using namespace std;int n 23333333;int main() {//枚举 0 出现的次数//因…...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...

安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)
船舶制造装配管理现状:装配工作依赖人工经验,装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书,但在实际执行中,工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...