Java并发编程系列18:多线程之生产者和消费者模式_信号灯法(wait/notify通知机制)
1、生产者消费者模式
生产者消费者问题(Producer-consumer problem),也称为有限缓冲问题(Bounded-buffer problem),是一个多线程同步问题的经典案例。该问题描述了两个共享大小缓冲区的线程---既所谓的“生产者”和“消费者”----在实际运行时会发生的问题。生产者的主要作用时生成一定量的数据放到缓冲区中,然后重复此过程。以此同时,消费者也在缓冲区消费这些数据。该问题的关键是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消费数据。
要解决这些问题,就必须让生产者在缓冲区满时休眠(要不干脆就放弃数据),等到下次消费者消耗缓冲区中的数据的时候,生产者才能被唤醒,开始往缓冲区添加数据。统一,也可以让消费者在缓冲区空时进入休眠,等到生产者往缓冲区添加数据之后,再唤醒消费者。如果解决办法不够完善,则容易出现死锁的情况。出现死锁时,两个线程都会陷入休眠,等待对方唤醒自己。
生产者和消费者模式:这其实是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖,互为条件
对于生产者,没有生产产品之前,要通知消费者等待。而生产了产品之后,又需要马上通知消费者消费。
对于消费者,消费之后,要通知生产者已经消费完成,需要继续生产新的产品给消费者消费。
在生产者和消费者问题中,仅有synchronizede是不够的
synchronized可阻止并发更新同一个共享资源,实现了同步
synchronized不能用来实现不同线程之间的消息传递(通信)
线程通信:并发协助模型“生产者/消费者模式”->可用信号灯法。
2、信号灯法
信号灯法判断生产者(消费者)是否能能够放入(取出)产品的依据是标志位。
设置一个标识,当标识为真的时候,消费者消费,生产者等待。当标识为假的时候,生产者生产,消费者等待.
Java提供了3个方法(java.lang.Object类的方法)解决线程之间的通信问题
方法名:这些方法只能放在同步里。
final void wait() 作用:表示线程一直等待,直到其他线程通知,与sleep不同,会释放锁。
final void wait(long timeout) 作用:指定等待的毫秒数
final void notify() 作用:唤醒一个处于等待状态的线程
final void notifyAll() 作用:唤醒同一个对象上所有调用wait()方法的线程,优先级别搞得线程优先调度。
3、Object的wait/notify+信号灯法代码示例
package runnable;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;/*** 一个场景,共同的资源* 生产这消费者模式,信号灯法* wait():等待,释放锁,sleep不释放锁* notify()/notifyAll():唤醒*/
class Tea{private String teaName; //奶茶//信号灯//flag=T,生产者生产,消费者等待,生产完成后通知消费。//flag-F,消费者消费,生产者等待,消费完成后通知生产。boolean flag=true; //信号灯public synchronized void Make(String teaName){if(!flag){ //生产者等待try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}//制作奶茶System.out.println(Thread.currentThread().getName()+",奶茶店制作了:"+teaName);//制作完成this.teaName=teaName;//通知消费者this.notifyAll();//信号灯设置为falsethis.flag=false;}public synchronized void Take(){if(flag){ //消费者等待try {this.wait();} catch (InterruptedException e) {e.printStackTrace();}}//消费者消费System.out.println(Thread.currentThread().getName()+",消费者拿走了:"+teaName);//通知生产者this.notifyAll();//设置信号灯为truethis.flag=true;}
}//生产者
class Maker implements Runnable{public Tea tea;public Maker(Tea tea){this.tea=tea;}@Overridepublic void run() {for(int i=0;i<10;i++){if (i%2==0){this.tea.Make("珍珠奶茶");}else{this.tea.Make("香草奶茶");}}}
}//消费者
class Taker implements Runnable{public Tea tea;public Taker(Tea tea){this.tea=tea;}@Overridepublic void run() {for(int i=0;i<10;i++){tea.Take();}}
}//多线程执行
public class TeaTrading {public static void main(String[] args){//共同的资源Tea tea=new Tea();//生产者和消费者对象,使用同样的资源Maker marker=new Maker(tea);Taker taker=new Taker(tea);new Thread(marker).start();new Thread(taker).start();}
}
在使用线程的wait()等待/notify()通知机制时,一般都要配合一个 boolean 变量值(或者其他能够判断真假的条件),在 notify 之前改变该 boolean 变量的值,保证了程序的正确性。
执行结果:
参考:一篇文章,让你彻底弄懂生产者--消费者问题 - 简书 (jianshu.com)
生产者消费者模式-Java实现 - 天目山电鳗 - 博客园 (cnblogs.com)
Java 多线程详解(四)------生产者和消费者 - YSOcean - 博客园 (cnblogs.com)
JAVA多线程之生产者消费者模型_L_R-CSDN博客_多线程生产者消费者
生产者、消费者问题(管程法与信号灯法) - 墨染念颖 - 博客园 (cnblogs.com)
Java并发编程系列18:多线程之生产者和消费者模式_信号灯法(wait/notify通知机制)相关推荐
- python并发编程之semaphore(信号量)_Python 并发编程系列之多线程
Python 并发编程系列之多线程 2 创建线程 2.1 函数的方式创建线程 2.2 类的方式创建线程 3 Thread 类的常用属性和方法 3.1 守护线程: Deamon 3.2 join()方法 ...
- Java并发编程系列
Java并发编程系列 2018-03-08 Java 并发编程:核心理论 Java并发编程:Synchronized及其实现原理 Java并发编程:Synchronized底层优化(轻量级锁.偏向锁) ...
- 并发编程系列之五多线程synchronized是可重复加锁,重入锁
并发编程系列之五多线程synchronized是可重复加锁,重入锁.对于重入锁的概念就是可以重复的加锁.. 示例1,在同一个类里面进行加锁,不同的方法调用,都一层一层的嵌套进行加锁,示例1演示重入锁的 ...
- Java 并发编程系列之带你了解多线程
早期的计算机不包含操作系统,它们从头到尾执行一个程序,这个程序可以访问计算机中的所有资源.在这种情况下,每次都只能运行一个程序,对于昂贵的计算机资源来说是一种严重的浪费. 操作系统出现后,计算机可以运 ...
- 『图解Java并发编程系列』10张图告诉你Java并发多线程那些破事
目录 线程安全问题 活跃性问题 性能问题 有态度的总结 头发很多的程序员:『师父,这个批量处理接口太慢了,有什么办法可以优化?』架构师:『试试使用多线程优化』第二天头发很多的程序员:『师父,我已经使用 ...
- java并发实战编程pdf_「原创」Java并发编程系列25 | 交换器Exchanger
2020年Java面试题库连载中 [000期]Java最全面试题库思维导图 [001期]JavaSE面试题(一):面向对象 [002期]JavaSE面试题(二):基本数据类型与访问修饰符 [003期] ...
- Java 并发编程系列之闭锁(CountDownLatch)
在讲闭锁之前,我们先来思考一个问题:在多线程环境下,主线程打印一句话,如何保证这句话最后(其他线程全部执行完毕)打印? 博主目前可以想到的实现方式有两种.一种是通过 join() 方法实现,另一种就是 ...
- reentrantlock非公平锁不会随机挂起线程?_【原创】Java并发编程系列16 | 公平锁与非公平锁...
本文为何适原创并发编程系列第 16 篇,文末有本系列文章汇总. 上一篇提到重入锁 ReentrantLock 支持两种锁,公平锁与非公平锁.那么这篇文章就来介绍一下公平锁与非公平锁. 为什么需要公平锁 ...
- Python并发编程系列之多线程
1 引言 上一篇博文详细总结了Python进程的用法,这一篇博文来所以说Python中线程的用法.实际上,程序的运行都是以线程为基本单位的,每一个进程中都至少有一个线程(主线程),线程又可以创建子线程 ...
最新文章
- as工程放到源码编译_UE4 Program 类型工程的限制和解决方法
- 国考中的电子信息类与计算机类,信息工程属于什么类-电子信息工程在公务员考试中属于计算机类吗 – 手机爱问...
- Linux系统调用--getrlimit()与setrlimit()函数详解
- yocto中文环境搭建
- Eclipse 报 “Exception in thread main java.lang.OutOfMemoryError: Java heap space ”错误的解决办法...
- Java实现前中后序线索化二叉树以及遍历
- 完整的POM文档内容
- MySQL中SELECT语句简单使用
- python3 collections数据类型模块
- 【OpenCV】OpenCV函数精讲之 -- 访问图像中的像素
- (102)FPGA面试题-如何选择FPGA型号?
- [设计模式-行为型]访问者模式(Vistor)
- iOS:UIWebView scrollView 的分页滑动问题
- 计算机网络笔记四 无线局域网
- 人工智能语音如何实现?
- 计算机系统维护要不要自动更新,电脑自动更新功能开启还是关闭,到底要不要关闭...
- linux的的符号,Linux 常见特殊符号
- 牛根生的“牛“,牛在哪里?
- 清理qmail邮件队列
- 输掉战役赢战争(博弈论的诡计)
热门文章
- “展厅三维全景”技术,将产品和企业文化以vr展示出来
- 移动应用测试篇(1)——移动应用的发展
- Web界面应用的测试内容
- 微信健身房小程序开发源码设计方案
- App Store Server API 实践总结
- C语言入门知识1(零基础新手适用)
- java与c的交互,java与c/c++之间的数据交互,java交互
- pgSQL查询语句ERROR: relation “XXX“ does not exist
- 树莓派Raspberry 4B+ 一篇快速搞定新版树莓派系统无屏幕初装+SSH连接+桌面显示
- Eclipse Maven clean后错误: 找不到或无法加载主类com.xxx.ShopApplication