// 以生产和消费烤鸭为例
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)

最新文章

  1. 枚举扩展方法获取枚举Description
  2. JAVA中SERIALVERSIONUID的解释
  3. Confluent介绍(二)--confluent platform quickstart
  4. webservice和restful的区别
  5. tcpdump命令--实用篇
  6. UVa 232 Crossword Answers
  7. layout cannot be resolved or is not a field
  8. classloader.getresources() 介绍
  9. System.AccessViolationException: 尝试读取或写入受保护的内存 解决办法
  10. django note
  11. css学习笔记二
  12. android adb经常使用的命令
  13. [Oracle] 常用工具集之 - SQL*Loader
  14. android Android性能优化之如何避免Overdraw
  15. Java多线程Master-Worker模式
  16. FFmpeg的H.264解码器源代码简单分析:宏块解码(Decode)部分-帧间宏块(Inter)
  17. Hibernate的二级缓存策略
  18. 洛谷P4770 [NOI2018]你的名字 [后缀自动机,线段树合并]
  19. python学习之路07
  20. Python 3下Matplotlib画图中文显示乱码的解决方法

热门文章

  1. LayerMask小结
  2. mybatis-config.xml文件详解
  3. 3.Queues(队列)
  4. 安装好Oracle和PLSQLDeveloper后,PLSQLDeveloper登录时没有可选数据库和连接为问题
  5. 奇怪的string
  6. pow函数
  7. PHP学习笔记(5)GD库画验证码
  8. 上传图片时,使用JS获得图片文件大小
  9. C++读取Sql Server
  10. plsql 环境搭建(sqlplus环境设置)