// 以生产和消费烤鸭为例
class Resource
{
private String name;
private int count = 1; // 记录烤鸭的编号
private boolean flag = false; public synchronized void set(String name)
{
if(flag)
try{this.wait();}catch(InterruptedException e){}
this.name = name + count;
count++;
System.out.println(Thread.currentThread().getName()+"..生产者.."+this.name);
flag = true;
this.notify();
} public synchronized void out()
{
if(!flag)
try{this.wait();}catch(InterruptedException e){}
Sytem.out.println(Thread.currentThread().getName()+ "...消费者.."+ this.name);
flag = false;
this.notify();
}
} class Producer implements Runnable
{
Resource r;
Producer(Resource r)
{
this.r = r;
} public void run()
{
while(true)
{
r.set("烤鸭");
}
}
} class Consumer implements Runnable
{
Resource r;
Consumer(Resource r)
{
this.r = r;
}
public void run()
{
while(true)
{
r.out();
}
}
} class ProducerConsumerDemo
{
public static void main(String[] args)
{
// 创建资源
Resource r = new Resource(); // 创建任务
Producer pro = new Producer(r);
Consumer con = new Consumer(r); // 多生产者
Thread t0 = new Thread(pro);
Thread t1 = new Thread(pro); // 多消费者
Thread t2 = new Thread(con);
Thread t3 = new Thread(con);
Thread t4 = new Thread(con);
Thread t5 = new Thread(con); // 开启线程
t0.start();
t1.start();
t2.start();
t3.start();
t4.start();
t5.start();
}
}

出现错误的两种情况:

  1. 线程安全问题(虚假唤醒): 线程1 生产的烤鸭被线程3 和线程5 两个线程同时消费

    • if 只能判断标记一次, 会导致不该运行的线程运行了, 出现数据错误的情况
    • while 可以多次判断标记, 解决了线程获取执行权后, 是否要运行的问题!

  1. 死锁

    • notify() 一次只能唤醒一个线程, 如果本方唤醒类本方, 没有意义. 而且 while 判断标记多次,

      会导致死锁.
    • notifyAll() 解决了本方线程一定会唤醒对方线程的问题.
// 升级版代码
class Resource
{
private String name;
private int count = 1; // 记录烤鸭的编号
private boolean flag = false; public synchronized void set(String name)
{
// 将 if 换为 while, 线程从冻结状态被唤醒后,需要判断 flag 标记之后, 确定是否继续生产"烤鸭"
while(flag)
try{this.wait();}catch(InterruptedException e){}
this.name = name + count;
count++;
System.out.println(Thread.currentThread().getName()+"..生产者.."+this.name);
flag = true;
this.notifyAll(); // 肯定会唤醒对方的线程, 解决了死锁问题
} public synchronized void out()
{
while(!flag)
try{this.wait();}catch(InterruptedException e){}
Sytem.out.println(Thread.currentThread().getName()+ "...消费者.."+ this.name);
flag = false;
this.notifyAll();
}
} class Producer implements Runnable
{
Resource r;
Producer(Resource r)
{
this.r = r;
} public void run()
{
while(true)
{
r.set("烤鸭");
}
}
}

- [JavaSE 基础视频(毕向东)](https://www.bilibili.com/video/av3106510/#page=4)

Java 多线程通信之多生产者/多消费者的更多相关文章

  1. java 多线程并发系列之 生产者消费者模式的两种实现

    在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题.该模式通过平衡生产线程和消费线程的工作能力来提高程序的整体处理数据的速度. 为什么要使用生产者和消费者模式 在线程世界里,生产者就是生产数据 ...

  2. JAVA多线程通信

    JAVA多线程通信 package com.frank.thread; /** * author:pengyan * date:Jun 16, 2011 * file:ProducerAndCusto ...

  3. java基础知识回顾之java Thread类学习(八)--java多线程通信等待唤醒机制经典应用(生产者消费者)

     *java多线程--等待唤醒机制:经典的体现"生产者和消费者模型 *对于此模型,应该明确以下几点: *1.生产者仅仅在仓库未满的时候生产,仓库满了则停止生产. *2.消费者仅仅在有产品的时 ...

  4. Java 多线程学习笔记:生产者消费者问题

    前言:最近在学习Java多线程,看到ImportNew网上有网友翻译的一篇文章<阻塞队列实现生产者消费者模式>.在文中,使用的是Java的concurrent包中的阻塞队列来实现.在看完后 ...

  5. windows多线程(十) 生产者与消费者问题

    一.概述 生产者消费者问题是一个著名的线程同步问题,该问题描述如下:有一个生产者在生产产品,这些产品将提供给若干个消费者去消费,为了使生产者和消费者能并发执行,在两者之间设置一个具有多个缓冲区的缓冲池 ...

  6. java多线程系列15 设计模式 生产者 - 消费者模式

    生产者-消费者 生产者消费者模式是一个非常经典的多线程模式,比如我们用到的Mq就是其中一种具体实现 在该模式中 通常会有2类线程,消费者线程和生产者线程 生产者提交用户请求 消费者负责处理生产者提交的 ...

  7. Java多线程之并发协作生产者消费者设计模式

    两个线程一个生产者个一个消费者 需求情景 两个线程,一个负责生产,一个负责消费,生产者生产一个,消费者消费一个 涉及问题 同步问题:如何保证同一资源被多个线程并发访问时的完整性.常用的同步方法是采用标 ...

  8. Java多线程—阻塞队列和生产者-消费者模式

    阻塞队列支持生产者-消费者这种设计模式.该模式将“找出需要完成的工作”与“执行工作”这两个过程分离开来,并把工作项放入一个“待完成“列表中以便在随后处理,而不是找出后立即处理.生产者-消费者模式能简化 ...

  9. 线程间的通信_多生产者多消费者问题_JDK1.5新特性_Lock

    对于同步代码块,对于锁的操作是隐式的但是在JDK1.5之前的这种方法效率有点低,判断会很多,后面升级之后有新的解决方案 jdk1.5以后将同步和锁封装成了对象,并将操作锁的隐式方式定义到了该对象中,将 ...

随机推荐

  1. [docker]一些经常或不经常用到的镜像启动方法-一些常用的docker启动方式

    一些经常或不经常用到的镜像启动方法 设置容器的TZ另一种办法 参考: https://github.com/spujadas/elk-docker/blob/master/start.sh ## ov ...

  2. Objective-C_Block

    一.Block语法 Block:块语法,本质上是匿名函数(没有名称的函数),Block变量存放函数的实现,通过Block变量能直接调⽤函数.标准C里面没有Block.C语言的后期扩展版本号.加⼊了匿名 ...

  3. 将json形式的时间字符串转换成正常的形式

    //重写time的getter方法 //判断addtime和当期的时间差 // < 60分钟  返回 n分钟前 // > 60分钟  返回 n小时前 //超过24小时  返回 -月-日 - ...

  4. CodeForces 586B Laurenty and Shop

    F - Laurenty and Shop Time Limit:1000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I ...

  5. flask os.environ 的作用

    使用环境变量取值, 是为了增强系统的适应性, 在某些场景下, 设置环境变量比较方便. 假如, 你有一套代码, 部署在不同的系统中, 恰好这些系统有权限且很容易地设置环境变量, 那么, 这时候通过环境变 ...

  6. 破解idea注册码

    添加 “0.0.0.0 account.jetbrains.com”到host, hosts位置:C:\Windows\System32\drivers\etc 获取注册码网址: http://ide ...

  7. 技巧C#

    1.     在CallBack之后保持滚动条的位置: 在Asp.Net1.1中,CallBack之后保持滚动条的位置是一件非常痛苦的事情,特别是页中有一个Grid并且想要编辑特定的行.为了不停留在想 ...

  8. 设置EntityFramework中decimal类型数据精度

    EF中默认的decimal数据精度为两位数,当我们数据库设置的精度大于2时,EF将只会保留到2为精度. e.g. 2.1999将会被保存为2.20 网上找到常见的方法为重写DbContext的OnMo ...

  9. 【BZOJ】3400: [Usaco2009 Mar]Cow Frisbee Team 奶牛沙盘队(dp)

    http://www.lydsy.com/JudgeOnline/problem.php?id=3400 既然是倍数我们转换成mod.. 设状态f[i][j]表示前i头牛modj的方案 那么答案显然是 ...

  10. 【Raspberry Pi】修改时区

    Raspberry Pi没有时钟模块,所以每次断电都会丢失时间,但它有联网获取时间的预设.但要修改默认时区 http://outofmemory.cn/code-snippet/2899/shumei ...