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

MATPOWER的安装详细教程 MATPOWER的安装包实际为一个程序包,安装教程很简单,详见本文。 阅读详情
------ 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四个线程都进入等待状态。

















新手必看!BUUCTF-PWN题实战:从零搭建Kali环境到成功RIP栈溢出 本文为CTF二进制安全新手提供了一份详实的BUUCTF-PWN入门指南。内容涵盖从零搭建Kali Linux环境、安装配置pwntools等核心工具,到深入浅出地讲解栈溢出原理与汇编基础,并手把手带领读者完成一道名为“rip”的经典栈溢出题目实战,成功利用漏洞获取系统权限。 阅读详情

相关推荐

子网划分详解

还不会子网划分?看这篇就够了,保姆级教程

m0_47584619的博客 6093

生产者消费者设计模式

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

weixin_62824507的博客 831

避坑指南:C#调用CodeSoft打印标签时常见的5个错误及解决方法

本文总结了C#调用CodeSoft打印标签时常见的5个核心错误及解决方案,涵盖环境配置、模板加载、变量赋值、打印机切换和资源管理。重点解析了如何避免COM对象创建失败、模板路径错误、变量名不匹配、打印机切换异常及内存泄漏等问题,帮助开发者实现稳定高效的标签打印集成。

weixin_29215815的博客 223

(四)生产者消费者模式

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

roothahh的博客 9788

Replay音频转换时samplerate缺失的解决方案指南

Replay音频转换时提示"No module named 'samplerate' This error is lazily reported, having originally occured in File"的解决方案

weixin_47445543的博客 2130

哨兵一号数据snap预处理_利用SNAP软件进行Sentinel-1A卫星微波影像的预处理

利用SNAP软件对雷达影像进行预处理,主要包括热噪声去除、轨道文件校正、辐射定标、滤波校正、多普勒地形校正,最终获得入射角和后向散射信息。数据:本次实验所用数据为哨兵一号(Sentinel-1A)干涉宽幅模式(Interferometric Wideswath,IW)下经过多视处理和地距转换的GRD格式产品一般处理流程如下:0 SNAP软件安装软件安装:安装成功后打开SNAP后如图所示:1 数据...

weixin_30000407的博客 1万+

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

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

xiedongchi的博客 487

Apache Hop环境搭建实战:从零到一的下载、安装与配置指南

本文详细介绍了Apache Hop环境搭建的全过程,包括下载、安装与配置指南。从选择正确的JDK17版本到设置关键环境变量,再到启动GUI界面和解决常见问题,提供了全面的实战指导。适合数据工程师和ETL开发者快速上手这一强大的数据集成工具。

weixin_42634811的博客 574

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

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

芝麻的博客 536

13、HTML 表格创建与样式设置全攻略

本文全面介绍了HTML表格的创建与样式设置方法,涵盖表格行列添加、边框控制、行和列分组(thead、tbody、tfoot、colgroup)、宽度与高度设置、内边距与间距调整,以及表格对齐方式。同时提供了最佳实践建议、常见问题解决方案及综合示例代码,帮助开发者构建结构清晰、样式美观且兼容性强的HTML表格。

blue的专栏 97

【YOLO目标检测全栈实战】68 模型量化:从FP32到INT8,精度不掉,速度翻倍

摘要: 模型量化能显著提升推理速度(如YOLOv5s帧率从15FPS提升至38FPS),但需避免三大误区:1)校准集需覆盖实际场景,而非随机采样;2)敏感层(如检测头)应保留FP32精度;3)预处理必须与量化前后一致。通过混合精度量化(Backbone用INT8,检测头用FP32)和逐通道量化,可实现<1%的mAP损失。关键步骤包括校准数据准备、量化感知训练及逐层精度评估,最终在Jetson Nano等边缘设备上实现速度与精度的平衡。

企鹅码农的专属播客,分享10多年的实战案例和经验解读,包含YOLO、RT-DETR、AI等各个工作领域。 146

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

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

u013989923的博客 321

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

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

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

Delphizhou 的专栏 2217

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

SAP B1中文培訓教材_SDK

公司内部培训资料,对开发工具 ,DIAPI, UIAPI,Add-ON有详细解说

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

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

xh416的专栏 835

NTscan扫描工具

ntscan是一个扫描器,可以扫描没有密码和弱密码的共享,先到配置文件中填写猜测的用户名和密码,扫描过程中右侧出现的是扫描日志。

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

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

爱.NET 271

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、付费专栏及课程。

余额充值