(三)(2)wait/notify实现生产者-消费者模型,join方法
生产者,消费者模型
举个例子来说明,厨师,服务员,厨师做菜,服务员上菜,如果厨师没有做好菜,那么服务员就无法上菜,厨师做好了菜,然后通知服务员消费(上菜)。在这个过程之中,厨师扮演的就是生产者,服务员扮演消费者。
一句话说:生产者没有生产出来东西,消费者就必须等待着,生产者生产出来了,就通知消费者进行消费。
很明显,消费者等待就对应我们的wait方法,生产者通知消费者对应notify方法,所以,生产者,消费者模型可以用notify,wait来实现。
那么与之对应的,生产者不可能只有一个,消费者也不可能只有一个,所以,他们之间就有了一对一,一对多,多对一,多对多关系。
一对一模型:
如下就是一个简单的一个生产者,一个消费者的模型:
MyList.java,用于存储数据
Produce.java:生产者
package 第三章_wait_join; //生产者
public class Produce {
private String lock;
public Produce(String lock){
this.lock=lock;
}
public void setValue(){
try{
synchronized (lock){
if(MyList.getSize()!=){ //无需生产
lock.wait();
}
//生产
String temp = "设置了值"+System.currentTimeMillis();
System.out.println(temp);
MyList.list.add(temp);
lock.notify(); //通知消费者
} }catch (InterruptedException e){
e.printStackTrace();
}
}
}
Reduce.java,消费者
package 第三章_wait_join; //消费者
public class Reduce {
private String lock;
public Reduce(String lock){
this.lock=lock;
}
public void getValue(){
try{
synchronized (lock){
if(MyList.getSize()==){ //等待生产
lock.wait();
}
//消费
System.out.println("获取到的值是:"+MyList.list.remove());
lock.notify(); //通知消费者
} }catch (InterruptedException e){
e.printStackTrace();
}
}
}
ThreadP生产线程:
package 第三章_wait_join; public class ThreadP extends Thread{
private Produce produce;
private Produce p;
public ThreadP(Produce p){
this.produce=p;
}
public void run(){
while(true){
produce.setValue();
}
}
}
ThreadR 消费线程:
package 第三章_wait_join; //消费者
public class Reduce {
private String lock;
public Reduce(String lock){
this.lock=lock;
}
public void getValue(){
try{
synchronized (lock){
if(MyList.getSize()==){ //等待生产
lock.wait();
}
//消费
System.out.println("消费者"+Thread.currentThread().getName()+"获取到的值是:"+MyList.list.remove());
MyList.list.remove();
lock.notify(); //通知消费者
} }catch (InterruptedException e){
e.printStackTrace();
}
}
}
test.java.
package 第三章_wait_join; import java.sql.ResultSet; public class test {
public static void main(String[] args){
try {
String lock = "lock";
Produce p = new Produce(lock);
Reduce r = new Reduce(lock);
ThreadP threadP = new ThreadP(p);
ThreadR threadR = new ThreadR(r);
threadP.start();
threadR.start();
ThreadR.sleep();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
运行结果:
可以看到,符合预期,生产一个,消费一个,轮换进行。
多生产,多消费情况:
问题:这种模式如果按照我们上面的写法,会出现一个问题,就是可能现在既有生产者,也有消费者在等待,这时候如果某一方发出了通知,比如生产者生产好了,发出通知,按道理是应该唤醒一个消费者的,但是也有可能唤醒其他生产者,而不是消费者,那这样很明显是不合理的,如果这样的次数多了,就会导致所有生产者和消费者都处于wait状态,那么这个时候程序就会出现假死状态。比如下将test.java更改为下面的代码,运行之后会发现程序运行一会就处于”假死”状态了。
package 第三章_wait_join; import java.sql.ResultSet; public class test {
public static void main(String[] args){
try {
String lock = "lock";
Produce p = new Produce(lock);
Reduce r = new Reduce(lock);
ThreadP[] threadP = new ThreadP[];
ThreadR[] threadR = new ThreadR[];
for(int i=;i<;i++){
threadP[i] = new ThreadP(p);
threadP[i].setName("生产者:"+i);
threadR[i] = new ThreadR(r);
threadR[i].setName("消费者:"+i);
threadP[i].start();
threadR[i].start();
}
ThreadR.sleep();
}catch (InterruptedException e){
e.printStackTrace();
}
}
}
解决办法:
使用notifyAll() 替换 notify每次将所有线程都唤醒,包括自己的异类,这样就能保证生产的都被消费,消费者都有所消费,不会出现上述状况。
一个生产者,多消费:
问题:这种情况下,会出现两个问题。
1.条件改变时,其他消费者没有及时得到响应,以为可以消费,继续执行下去,这个时候如果是一个数组之类的,就会出现越界的异常,,,,这个没太看懂。
2.和多生产多消费一样,消费者唤醒时候可能唤醒同类,同类没有可以消费的,继续等待,下一个同类也没有可以消费的,继续等待,最后导致全部同类等待,生产者也等待,没有能够notify的,自然假死,
解决:
1.将判断条件是否成立的语句改为while循环进行判断
2.notifyAll()唤醒所有线程
多生产,一个消费:
这种一般没有什么问题,不会假死,因为就算生产者唤醒了生产者,然后发现还没有被消费,又等待,以此类推,所有生产者都等待了,但是最后总会唤醒消费者,消费者总能消费,消费之后总能唤醒一个生产者进行生产。
Join:
这个方法是调用该方法的线程执行完毕之后,当前线程再执行接下来的语句,有下面几个特点:
1.会释放锁,换到另一个线程执行,相比之下sleep并不会释放锁。原因是join内部是使用wait实现的,wait会释放锁
2.和wait一样,已经处于阻塞状态了,就不能再用其他方法让该线程处于阻塞状态
3.join(long) long时间之后,如果目标线程还没有执行完,则取消阻塞状态,进入就绪状态
4.同时使用sleep和join(long)可能会出现join后面的代码提前之前,原因是可能join(long)在竞争锁的过程之中,没有竞争过其他线程,其他线程之中使用了slee(long2)long2 > long,这就意味着join已经过了有效时间,所以不再阻塞,而是和调用join的线程竞争,竞争自然可能有很多结果了
。。不放代码了,麻烦
(三)(2)wait/notify实现生产者-消费者模型,join方法的更多相关文章
- 多线程-4.wait() notify() notifyAll() 生产者消费者模型
1.wait()方法 该方法继承于Object类.在调用obj.wait()方法后,当前线程会失去obj的锁.待其他线程调用obj.notify()或notifyAll()方法后进入锁等待池,争抢到锁 ...
- Java多线程使用wait和notify实现生产者消费者模型
Java多线程使用wait和notify这两个关键字的学习,通过实现生成者与消费者来成对研究比较科学. 从两个字的意义来讲就是等待与通知这个简单道理. 现在先模拟一个缓存区存储,是用一个list实现的 ...
- Java多线程14:生产者/消费者模型
什么是生产者/消费者模型 一种重要的模型,基于等待/通知机制.生产者/消费者模型描述的是有一块缓冲区作为仓库,生产者可将产品放入仓库,消费者可以从仓库中取出产品,生产者/消费者模型关注的是以下几个点: ...
- java多线程15 :wait()和notify() 的生产者/消费者模式
什么是生产者/消费者模型 一种重要的模型,基于等待/通知机制.生产者/消费者模型描述的是有一块缓冲区作为仓库,生产者可将产品放入仓库,消费者可以从仓库中取出产品,生产者/消费者模型关注的是以下几个点: ...
- python2.0_s12_day9之day8遗留知识(queue队列&生产者消费者模型)
4.线程 1.语法 2.join 3.线程锁之Lock\Rlock\信号量 4.将线程变为守护进程 5.Event事件 * 6.queue队列 * 7.生产者消费者模型 4.6 queue队列 que ...
- 如何在 Java 中正确使用 wait, notify 和 notifyAll – 以生产者消费者模型为例
wait, notify 和 notifyAll,这些在多线程中被经常用到的保留关键字,在实际开发的时候很多时候却并没有被大家重视.本文对这些关键字的使用进行了描述. 在 Java 中可以用 wait ...
- Python之路(第三十八篇) 并发编程:进程同步锁/互斥锁、信号量、事件、队列、生产者消费者模型
一.进程锁(同步锁/互斥锁) 进程之间数据不共享,但是共享同一套文件系统,所以访问同一个文件,或同一个打印终端,是没有问题的, 而共享带来的是竞争,竞争带来的结果就是错乱,如何控制,就是加锁处理. 例 ...
- 生产者消费者模型——wait/notify/notifyAll使用
告警系统架构如下 1. 数据处理系统处理完原始数据并入库后,发送消息到kafka系统: 2. 告警生产者从kafka系统查询消息存入告警消息队列: 3. 告警消费者从告警消息队列查询消息进行处理. 这 ...
- 多线程学习-基础(十二)生产者消费者模型:wait(),sleep(),notify()实现
一.多线程模型一:生产者消费者模型 (1)模型图:(从网上找的图,清晰明了) (2)生产者消费者模型原理说明: 这个模型核心是围绕着一个“仓库”的概念,生产者消费者都是围绕着:“仓库”来进行操作, ...
随机推荐
- 注解@ConfigurationProperties使用方法
注解@ConfigurationProperties使用方法 前言 最近在思考使用java config的方式进行配置,java config是指基于java配置的spring.传统的Spring一般 ...
- Python保存时提示“SyntaxError: Non-ASCII character '\xe8' in file”
原因 Python 默认的是ASCII 编码方式,如果出现中文会出现问题,所哟必须在代码的第二行或第一行 显示的声明编码方式(已注释的方式,即"#"开始) 解决方法 在文件头部加上 ...
- CentOS6.5源码安装mysql-5.5.21
本文参考自 http://www.cnblogs.com/ShanFish/p/6531365.html,但不局限于它. 一. 卸载旧版本 .检查是否安装mysql组件 # rpm -qa | gre ...
- 【转】SQL语句面试题
1.一道SQL语句面试题,关于group by表内容:2005-05-09 胜2005-05-09 胜2005-05-09 负2005-05-09 负2005-05-10 胜2005-05-10 负2 ...
- 如何利用Map2Shp进行快速格式转换
有时,用户仅需要进行GIS数据格式的简单转换,对文字注记.制图表达.投影信息无特别要求,可进行快速格式转换.做为MapGIS文件与Shape文件间的格式转换工具,Map2Shp软件操作过程十分简单,只 ...
- Java 遍历集合时产生的ConcurrentModificationException异常
前几天做Java实验的时候,打算用foreach遍历一个ArrayList集合,并且当集合中的某个元素符合某个值时删除这个元素.写完运行时抛出了ConcurrentModificationExcept ...
- 《企业IT架构转型之道:阿里巴巴中台战略思想与架构实战》-总结
一.什么是业务中台 概念来自于阿里,介于前台和后台(此后台指的是云计算.数据库.消息队列.缓存等基础服务) 采用共享式架构设计解决以往烟囱式架构设计的资源浪费.重复造轮.试错成本高的问题 阿里的中 ...
- Centos7.6部署k8s v1.16.4高可用集群(主备模式)
一.部署环境 主机列表: 主机名 Centos版本 ip docker version flannel version Keepalived version 主机配置 备注 master01 7.6. ...
- OAuth2.0的那点荒唐小秘密 -几个简单概念和原理
OAuth2.0这个名词你是否在项目中时常听到呢?是否觉得好像懂,又好像不太懂呢? 最近一直想写篇关于OAuth2.0的东西,记录下我的学习与感悟,然各种理由的拖延,直到今日才静下心来写下这篇博客.当 ...
- maven-assembly-plugin入门
愿文地址:https://www.jianshu.com/p/e8585a991e8e 当你使用 Maven 对项目打包时,你需要了解以下 3 个打包 plugin,它们分别是 plugin func ...