黑马程序员——利用生产者和消费者模式讨论notify()方法的运用

【VS Code+Verilog+Vivado使用】(1)常用插件 VS Code + Verilog + Vivado联合使用说明 阅读详情
------ Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

    当使用多线程来同时运行多个任务时,可以通过锁来同步多个任务的行为,从而使得一个任务不会干涉另一个任务的资源。也就是说,如果多个任务在交替着访问内存中的某项资源,可以通过锁来控制同一时刻只有一个任务可以访问该资源。为了使任务可以协作,我们可以使用wait()和notify()机制来控制。

     这里,我们用生产者和消费者的例子来说明:

/*
	定义资源类,类中包括生产set和消费out的方法
*/
class Resource{
	private String name;
	private  int count = 1;
	private boolean flag = false;
	public synchronized void set(String name){
		while(flag)
			try{
			 wait();
		}catch(InterruptedException e){
			e.printStackTrace();
		}
		this.name = name+count++;
		System.out.println(Thread.currentThread().getName()+"生产"+this.name);
		flag = true;
		notifyAll();//此处若改为notify()方法,可能会使所有线程都陷入等待状态
	}
	public synchronized void out(){
		while(!flag)
			try{
			wait();
		}catch(InterruptedException e){
			e.printStackTrace();
		}
		
		System.out.println("..."+Thread.currentThread().getName()+"消费...."+this.name);
		flag = false;
		notifyAll();
	}
}
//定义生产者类,实现Runnable接口的run方法
class Producer implements Runnable{
	private Resource res;
	Producer(Resource res){
		this.res = res;
	}
	public void run(){
		while(true)
			res.set("产品");
	}

}
//定义消费者类,实现Runnable接口的run方法
class Consumer implements Runnable{
	private Resource res;
	Consumer(Resource res){
		this.res = res;
	}
	public void run(){
		while(true)
			res.out();
	}
}
public class Noname1{
	public static void main(String[] args){
		Resource res = new Resource();
		Producer pro = new Producer(res);
		Consumer con = new Consumer(res);
		//创建两个生产者线程
		Thread t0 = new Thread(pro);
		Thread t1 = new Thread(pro);
		//创建两个消费者线程
		Thread s2 = new Thread(con);
		Thread s3 = new Thread(con);
		//分别启动线程
		t0.start();
		t1.start();
		s2.start();
		s3.start();
		
	}
}
     

       上面的例子定义了两个生产者和两个消费者,

生产者生产一个产品,消费者消费一个,它们可

以很和谐地工作。运行结果如图。

        我们在set和out方法里使用的唤醒等待线程

的方法是notifyAll(),我们知道,如果使用notify()

的话,由于每次只能唤醒单个线程,所以最终可

能导致所有线程都陷入等待状态。

       可是,问题是,如果同时有多个线程处于等

待状态,notify()方法会唤醒哪个线程呢?为此,

我查阅了一些资料,有的说是随机唤醒一个线程,

有的说是唤醒最先等待的线程,还有的对此问题

避而不谈,下面,我们通过具体实例来验证,还

是使用的生产者与消费者的例子。


      为了验证上面的问题,我们将上面的代码进行了一些改变

class Resource{
	private String name;
	private  int count = 1;
	private boolean flag = false;
	public synchronized void set(String name){
		while(flag)
			try{
			 wait();
		}catch(InterruptedException e){
			e.printStackTrace();
		}
		this.name = name+count++;
		System.out.println(Thread.currentThread().getName()+"生产"+this.name);
		flag = true;
		notify();
	}
	public synchronized void out(){
		while(!flag)
			try{
			wait();
		}catch(InterruptedException e){
			e.printStackTrace();
		}
		
		System.out.println("..."+Thread.currentThread().getName()+"消费...."+this.name);
		flag = false;
		notify();
	}
}
class Producer implements Runnable{
	private Resource res;
	Producer(Resource res){
		this.res = res;
	}
	public void run(){
		while(true)
			res.set("产品");
	}

}
class Consumer implements Runnable{
	private Resource res;
	Consumer(Resource res){
		this.res = res;
	}
	public void run(){
		while(true)
			res.out();
	}
}
public class ProducerDemo{
	public static void main(String[] args){
		Resource res = new Resource();
		Producer pro = new Producer(res);
		Consumer con = new Consumer(res);
		//创建两个生产者线程
		Thread t0 = new Thread(pro);
		Thread t1 = new Thread(pro);
                //创建两个消费者线程
		Thread t2 = new Thread(con);
		Thread t3 = new Thread(con);
	
		 t0.start();
		 try{
			 Thread.sleep(10);
			 }catch(InterruptedException e){
 				e.printStackTrace();
 			}
 		t1.start();
 		//让主线程停止10ms,目的是使两个生产者线程都陷入等待
		 try{
			 Thread.sleep(10);
 			}catch(InterruptedException e){
				 e.printStackTrace();
 			}
		t2.start();
		try{
			Thread.sleep(10);
			}catch(InterruptedException e){
				e.printStackTrace();
			}
		t3.start();
		
	}
}

      其中,其他内容不变,只有主方法中插入了一些sleep()方法,目的是使各个线程按照我们自己的意愿去运行。我们先看运行结果如图。下面我们来分析:

      首先,语句 t0.start()启动t0线程,生产出产品1,t0陷入等待;

      接着t1线程启动,由于flag被置为true,所以,不生产产品,t1直接等待;

     然后t2线程启动,消费产品1,将flag置为flase,唤醒一个线程,t2等待;

     根据输出,我们可以看到t2唤醒的是线程是t0,所以t0执行,生产产品2,唤醒一个线程,t0再次等待;

     根据输出,t0唤醒的线程并没有执行,而是直接进入等待,接着执行t3,消费产品2,唤醒一个线程,t3等待;

     根据输出,t3唤醒的线程也没有执行,直接陷入等待,自此,所有的线程都在等待状态,所以程序停滞不前。

    

     为了更好的说明问题,我们通过画图来解释:


       根据结果我们可以推论,用来存储等待中的线程的容器为一队列,即履行“先进先出”的原则。如上图所示,从下至上为进入等待状态线程的顺序。当执行notify()方法时,也是从下至上的顺序。可以看到,最终,t0,t1,t2,t3四个线程都进入等待状态。

















疾风气象大模型4.0白皮书 《疾风气象大模型白皮书摘要》 疾风气象大模型(J-AFM)是国内首个基于3D建模的气象智能平台,突破传统二维气象分析的局限,采用体素化技术构建"x-y-z-t"四维统一模型,实现从分钟级短临到30天跨时效的连续预测。核心技术包括:1)多源数据三维同化,融合雷达、卫星、风廓线等异构数据;2)物理约束AI解码器,嵌入能量守恒、湍流封闭等气象原理;3)行业适配插件,服务风电、光伏、航空等领域,如山地风场尾流校正、云影短临预测。平台支持批流一体推理与边缘部署,提供概率化输出和误差溯源能力,显著降 阅读详情

相关推荐

手把手教你用WKT文件在Petrel中创建自定义坐标系统(附CGCS2000转换模板)

本文详细介绍了如何在Petrel软件中利用WKT文件创建自定义坐标系统,并重点提供了CGCS2000坐标系的完整转换模板与实战步骤。通过解析坐标系统的三大支柱(大地坐标系、投影坐标系、基准转换)和WKT文件结构,指导用户从零开始构建适用于本土化项目的坐标系统,确保地质建模与空间数据处理的精度与一致性。

gpt4scribbler的博客 279

生产者消费者设计模式

生产者消费者模式实现 (1)仓库类 (2)生产者线程类 (3)消费者线程类 (3)测试类

weixin_62824507的博客 831

【机械臂算法】机械臂动力学参数辨识仿真

本文以puma560m机械臂为例子进行动力学参数辨识的讲解,puma560m可以在robotic toolbox中找到,这里以它真实机械臂对他的动力学参数进行辨识。此外这里还有要说的是,机械臂参数辨识其实是一个系统工程,其中和系统的机械、电子、嵌入式都有着很深的联系,并不是仿真这么简单的。

vrijheid的博客 7834

(四)生产者消费者模式

(一)生产者消费者模式原理: 在一个系统中,存在生产者和消费者两种角色,他们通过内存缓冲区进行通信,生产者生产消费者需要的资料,消费者把资料做成产品。生产消费者模式如下图: (二)代码实现 生产者是一堆线程,消费者是另一堆线程,内存缓冲区可以使用List数组队列,数据类型只需要定义一个简单的类就好。最关键就是内存缓冲区为空的时候消费者必须等待,而内存缓冲区满的时候,生产者必须等待。其他时候可以是个动态平衡:   首先设置Market类: 其中变量有:Max(仓库最大值),List(用于存放数据) 方法有

roothahh的博客 9788

OpenCV视频防抖算法实现与优化指南

本文详细解析了如何使用OpenCV实现视频防抖算法。通过特征点检测与光流跟踪估算帧间运动,利用滑动平均平滑运动轨迹,并应用仿射变换重塑视频帧。文章提供了完整的代码实现、参数调优指南及黑边处理等优化技巧,帮助开发者高效处理手持拍摄等场景下的视频抖动问题。

weixin_29062823的博客 341

IEC104协议学习专栏内容规划

IEC104协议的编程学习

sixtome 1110

黑马程序员——Java---多线程-生产者和消费者

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ------- /*两个实现Runnble接口的类,一个是生产者 maker ,一个是消费者 consumer ,分别覆盖了Runnable的run方法,创建线程的运行代码 生产者每生产一个产品就在控制台里面打印出来,然后停止1秒钟。 生产者每生产一个产品消费者就消费一个产品 然后

xiedongchi的博客 487

Prometheus+Grafana+alertmanager+PrometheusAlert监控告警 实现飞书告警

以上 是以grafana做的邮件告警,但是感觉邮件很少人看。下面是以webhook 可以接入。启动alertmanager (设置 开机在启动)下载prometheusalter 配置文件。这里的告警是基于grafana上搭建的。我们启动PrometheusAlert。写完之后进行重启Prometheus。prometheus 的告警规则。测试 基于我们生产环境做的文档。然后在grafana上导模板。上导入模板就可以 直接引用。访问 grafana页面。重启一下grafana。

墨痕诉清风的博客 859

生产者与消费者模式--课后程序(Python程序开发案例教程-黑马程序员编著-第13章-课后作业)

生产者与消费者模式是多线程同步应用的经典案例,它通过一个固定大小的缓冲区解决了代表“生产者”和代表“消费者”的两个线程在实际运行时发生的强耦合问题——由于生产者的生产能力与消费者的消费能力互不匹配,导致双方必须互相阻塞等待处理。在生产者与消费者模式中,生产者与消费者彼此之间通过缓冲区进行通讯

芝麻的博客 536

微信小程序 - 详细实现可移动悬浮按钮,全屏悬浮元素图标可拖动拖拽并自动吸附边缘,微信小程序实现悬浮球吸附边缘+计算靠边靠拢效果(详细代码,拖拽中途放手,靠左就自动跑到屏幕左侧,反之靠右跑到屏幕右侧)

微信小程序,vue2,vue3,微信小程序悬浮功能小按钮,自动吸附边缘效果,微信小程序之可拖动悬浮按钮的实现,如何实现靠近边缘自动吸住功能,小程序右下角的悬浮效果,实现可拖动悬浮图标,自动向两边靠拢,悬浮按钮的实现,微信小程序悬浮球效果,微信小程序(可拖动悬浮按钮)当靠近屏幕边缘时会磁吸靠近,如何实现全局悬浮按钮,悬浮球判断吸附在左右,全屏悬浮能拖着走,小程序怎么做悬浮图标靠近左右两侧能吸附住,悬浮菜单按钮,悬浮球可拖拽,全局悬浮球,全局悬浮框,实现悬浮按钮可拖动,自动吸附边缘效果,若按钮此时位于页面中间偏

🏆 前端领域优质创作者 1万+

手把手教你学Simulink--使用Simulink进行双冗余电机驱动系统的故障切换验证

提高系统可靠性:当一个电机或其驱动器出现故障时,另一个可以立即接管,避免系统停机。增强安全性:对于一些关键任务的应用场景(如飞行控制),确保即使在硬件故障情况下也能维持基本操作能力。简化维护:允许在不影响系统正常工作的情况下进行维护和更换故障部件。确定可能发生的故障类型(如过流、欠压、温度过高),以及每种故障对应的条件。通过上述步骤,我们简要介绍了如何基于Simulink实现双冗余电机驱动系统的故障切换验证。

MHD0815的博客 439

黑马程序员--生产者和消费者模式

关于生产者和消费者的理解

u013989923的博客 321

从零开始利用树莓派+扬声器,实现简单的蓝牙音箱,手机连接放歌

树莓派4B自带蓝牙和Wifi, 无需外接 USB dongle;蓝牙最常见的应用是近距离传输数据,比如蓝牙传文件,蓝牙音箱等。正好家里有个普通的usb供电的便携音箱;本文用树莓派蓝牙+普通音箱,实现简单的蓝牙音箱。怎样理解这三层:可以类比Linux网络层:ALSA 类似网络驱动框架A2DP 类似TCP/UDP层PulseAudio 类似HTTP层的服务器,类比Apache而蓝牙连接类似http连接和会话;

南七小僧的学海无涯 2221

自动编译安装Delphi控件批处理

echo off set COMPONENTDIR=E:\DELPHIVCL set BPL=%COMPONENTDIR%\BPL set SUIPACKSOURCEDIR=%COMPONENTDIR%\SUIPack43\Source set COOLTRAYSOURCEDIR=%COMPONENTDIR%\COOLTRAY rem 其实安装Delphi控件不过就是在注册表的指

Delphizhou 的专栏 2218

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

用DELPHI通过写注册表来实现建立IIS的虚拟目录!

    网上有很多关于用DELPHI来建立IIS的虚拟目录的例子,但都是需要加载类库,我这里则采用写注册表的方式来实现,方法简单,主要是不需要加入类的复杂过程,唯一的不同是需要重启电脑后才能生效。代码如下:procedure TForm1.Button1Click(Sender: TObject);var  RegODBC:TRegistry;  registerTemp : TRegistr

xh416的专栏 835

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

在非XP操作系统下模拟的LockWorkStation函数

在非XP操作系统下模拟的LockWorkStation函数 文章作者:Delphiscn 信息来源:邪恶八进制信息安全团队 程序功能:可在非XP的操作系统下所定计算机 (为了程序的界面美观,我使用了SUIPack控件,有兴趣的朋友可以去Delphibox.com下载) *********************************************************...

爱.NET 271

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

Delphi中代替WebBrowser控件的第三方控件

这几天,接触到在delphi中内嵌网页,用delphi7自带的TWebBrowser控件,显示的内容与本机IE8显示的不一样,但是跟装IE8之前的IE6显示一个效果。现在赶脚是下面两个原因中的一个: 1.Navigate这个方法用的有点问题,里面的参数不同及Navigate2等不同方法,调用的IE内核版本不同 2.这个自带的控件用着不爽,直接换一个第三方控件 对于第一点,找了半天冒...

ajv66295的博客 242

AD Blocker Trial 注册算法

转自:http://www.kanxue.com/bbs/showthread.php?t=154770 标 题: 【原创】AD Blocker Trial 注册算法 作 者: iltgcl 时 间: 2012-08-16,16:12:07 链 接: http://bbs.pediy.com/showthread.php?t=154770 【文章标题】: 【原创】AD Block

bberdong的专栏 956

黑马程序员_2_多线程之生产者与消费者

生产者与消费者

浮云只想听你说 569

黑马程序员——分析匿名内部类使用外部定义的对象

在分析之前,首先说一下内部类定义在局部的特点。         内部类被定义在局部时:        1. 不可以被成员修饰符修饰;        2. 可以直接访问外部类中的成员,因为还持有外部类中的引用;        3. 但是不可以访问它所在局部中的变量,只能访问被final修饰的局部变量。     其中,匿名内部类其实就是一条特殊的语句,被定义在方法中,所以,匿名内部类也属于被

codeting的博客 2262

黑马程序员——求两个字符串最大公约数的解法

题目:键盘输入两个字符串,获取这两个字符串的最大公约数并打印出来。例如:输入ijsfajk 和 qhsfqal ,输出的最大公约数是sf

codeting的博客 890

黑马程序员——网络编程之UDP

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ------- UDP协议        用户数据报协议(User Datagram Protocol, UDP)是一种不可靠的、无连接的协议,可以保证应用程序进程间的通信。与同样处在传输层的面向连接的TCP相比,UDP是一种无连接的协议,它的错误检测功能要弱得多。可以这样说,TCP有助于提供可靠性,而UD

codeting的博客 636

黑马程序员——网络编程之TCP

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ------- TCP协议         TCP(Transmission Control Protocol, 传输控制协议)是整个TCP/IP协议族中最重要的协议之一。它在IP提供的不可靠数据服务的基础上为应用程序提供了一个可靠的、面向连接的、全双工的数据传输服务。 TCP实现可靠性的方式     

codeting的博客 597

黑马程序员——数组笔记

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ------- 数组定义        概念:同一种类型数据的集合,其实数组就是一个容器。好处就是数组可以自动为其中的元素进行编号(从0开始)。    定义格式:数据类型[]  数组名 =  new 数据类型[元素个数或数组长度]        例如:定义一个用来存储3个整数的数组     int[]

codeting的博客 589

黑马程序员——单例设计模式

单例模式       单例模式,顾名思义,当需要保证某个类只能有一个实例时,就要使用单例。

codeting的博客 554

黑马程序员——关于数据结构栈的实现方式的探讨

数据结构栈         栈是限制插入和删除只能在一个位置上进行的表。该位置是表的末端,叫做栈顶(top)。对栈的基本操作有进栈(push)和出栈(pop),前者相当于插入,后者相当于删除最后插入的元素。        栈有时又叫做LIFO(后进先出)表。最基本的操作是进栈和出栈,还有一些其他操作,例如清空栈、判断栈是否为空、返回栈中元素的位置等,都作为栈的操作指令系统的一部分。 栈的实现

codeting的博客 540
上一篇: 黑马程序员——单例设计模式
下一篇: 黑马程序员——关于数据结构栈的实现方式的探讨
codeting
博客等级 码龄12年 2粉丝 11原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值