// 以生产和消费烤鸭为例
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. 点滴积累【JS】---JS小功能(JS实现模仿微博发布效果)

    效果: 思路: 利用多功能浮动运动框架实现微博效果,首先,将textarea中的属性添加到新创建的li里面然后,再将li添加到ul里面,再利用浮动运动框架将数据动态的显示出来. 代码: <hea ...

  2. 点滴积累【C#】---C#实现下载word

    效果: 思路: 简单的有两种方式下载,一种是流下载,一种是WriteFile下载.以下是使用WriteFile下载. 代码: protected void LinkButton1_Click(obje ...

  3. Atitit.http连接合并组件   ConnReducerV3 新特性

    Atitit.http连接合并组件   ConnReducerV3 新特性 D:\0workspace\AtiPlatf_cms\src\com\attilax\util\ConnReducerV2. ...

  4. Linux中如何设置服务自启动?

    转自:Linux中如何设置服务自启动? 有时候我们需要Linux系统在开机的时候自动加载某些脚本或系统服务,主要用三种方式进行这一操作: ln -s             在/etc/rc.d/rc ...

  5. springmvc-servlet.xml

    <?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.sp ...

  6. volatile关键字是什么意思

    我写了一段简单的对比代码并分别用vs2017以release方式编译然后用IDA观察汇编代码,如下图所示: 代码1 int a=5; printf("%d",a) 代码2 vola ...

  7. android 学习之rxjava

    最近在做的一个项目,所用到的技术和rxjava有关,因此还特地的学习了下,在这简单的介绍下rxjava: RxJava是什么 RxJava 在 GitHub 主页上的自我介绍是 "a lib ...

  8. Sql Server根据表名生成查询的存储过程(查询条件可选)

    static void Main(string[] args) { string 表名 = "water_emstime"; string sql = "exec Get ...

  9. phpQuery对数据信息的采集进一步学习

    前提:需要下载:phpQuery/phpQuery.php 链接:http://www.cnblogs.com/wuheng1991/p/5145398.html 1.对于规则的部分 <?php ...

  10. Dynamics CRM 系统自己定义部分的语言翻译

    Dynamics CRM 自带语言切换功能,在官网下载所需语言包安装后,在设置语言中就能看到你所加入的语言.勾选要启用的语言应用就可以.再打开系统设置--语言就能看到可更改用户界面语言的显示了. wa ...