8、生产者与消费者案例
来源:互联网 发布:云服务器开放443端口 编辑:程序博客网 时间:2024/06/05 18:11
厨师制作食物,放在取餐口让服务员取走食物,两个角色最终共享的数据即是食物
首先模拟生产消费的过程,未加同步,所以会出现两方面的问题:
1、食物与对应描述混乱
2、本来应该每样菜品生成5次,取出的菜品数量对不上(桑拿滑肉片7次,锅包肉3次)
package com.chocus.demo1;public class ProductCustomerDemo { public static void main(String[] args) { Food food = new Food(); Customers c = new Customers(food); Producter p = new Producter(food); Thread t1 = new Thread(c); Thread t2 = new Thread(p); t1.start(); t2.start(); }}/** * 消费者 * * @author Chocus * */class Customers implements Runnable { private Food food; public Customers(Food food) { this.food = food; } @Override public void run() { for (int i = 0; i < 10; i++) { food.get(); } }}/** * 生产者 * * @author Chocus * */class Producter implements Runnable { private Food food; public Producter(Food food) { this.food = food; } @Override public void run() { // 模拟生成食物,单数一种,双数一种 for (int i = 0; i < 10; i++) { if (i % 2 == 0) { food.set("锅包肉", "酸甜可口"); } else { food.set("桑拿滑肉片", "锤儿很喜欢吃"); } } }}/** * 食物:生产者和消费者共享的数据 * * @author Chocus * */class Food { /** * 名称 */ private String name; /** * 描述 */ private String desc; /** * 生产食物 */ public void set(String name, String desc) { this.name = name; // 模拟生成食物的过程 try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } this.desc = desc; } /** * 取走食物 */ public void get() { // 模拟取走食物的过程 try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("取走食物" + name + ",食物信息:" + desc); } public Food(String name, String desc) { this.name = name; this.desc = desc; } public Food() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; }}
结果
取走食物桑拿滑肉片,食物信息:酸甜可口取走食物锅包肉,食物信息:锤儿很喜欢吃取走食物桑拿滑肉片,食物信息:酸甜可口取走食物锅包肉,食物信息:锤儿很喜欢吃取走食物桑拿滑肉片,食物信息:酸甜可口取走食物锅包肉,食物信息:锤儿很喜欢吃取走食物桑拿滑肉片,食物信息:酸甜可口取走食物桑拿滑肉片,食物信息:酸甜可口取走食物桑拿滑肉片,食物信息:酸甜可口取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
经过简单分析可以发现,问题出在生产食物的set方法和取走食物的get方法线程不安全造成
那么在这两个方法上加同步后根据结果发现:
1、每样菜品生成5次的问题解决了,这证明加了同步后,不存在线程安全问题,及不会出现生产一半被取走的情况
2、发现出现null的情况,即还未生产菜品,菜品就被取走了
package com.chocus.demo1;public class ProductCustomerDemo { public static void main(String[] args) { Food food = new Food(); Customers c = new Customers(food); Producter p = new Producter(food); Thread t1 = new Thread(c); Thread t2 = new Thread(p); t1.start(); t2.start(); }}/** * 消费者 * * @author Chocus * */class Customers implements Runnable { private Food food; public Customers(Food food) { this.food = food; } @Override public void run() { for (int i = 0; i < 10; i++) { food.get(); } }}/** * 生产者 * * @author Chocus * */class Producter implements Runnable { private Food food; public Producter(Food food) { this.food = food; } @Override public void run() { // 模拟生成食物,单数一种,双数一种 for (int i = 0; i < 10; i++) { if (i % 2 == 0) { food.set("锅包肉", "酸甜可口"); } else { food.set("桑拿滑肉片", "锤儿很喜欢吃"); } } }}/** * 食物:生产者和消费者共享的数据 * * @author Chocus * */class Food { /** * 名称 */ private String name; /** * 描述 */ private String desc; /** * 生产食物 */ public synchronized void set(String name, String desc) { this.name = name; // 模拟生成食物的过程 try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } this.desc = desc; } /** * 取走食物 */ public synchronized void get() { // 模拟取走食物的过程 try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("取走食物" + name + ",食物信息:" + desc); } public Food(String name, String desc) { this.name = name; this.desc = desc; } public Food() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; }}结果
取走食物null,食物信息:null取走食物锅包肉,食物信息:酸甜可口取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃取走食物锅包肉,食物信息:酸甜可口取走食物锅包肉,食物信息:酸甜可口取走食物锅包肉,食物信息:酸甜可口取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
根据问题分析可以发现,菜品还未生产就被取走,那么就需要先生产菜品,生产完毕后再取走
package com.chocus.demo1;public class ProductCustomerDemo { public static void main(String[] args) { Food food = new Food(); Customers c = new Customers(food); Producter p = new Producter(food); Thread t1 = new Thread(c); Thread t2 = new Thread(p); t1.start(); t2.start(); }}/** * 消费者 * * @author Chocus * */class Customers implements Runnable { private Food food; public Customers(Food food) { this.food = food; } @Override public void run() { for (int i = 0; i < 10; i++) { food.get(); } }}/** * 生产者 * * @author Chocus * */class Producter implements Runnable { private Food food; public Producter(Food food) { this.food = food; } @Override public void run() { // 模拟生成食物,单数一种,双数一种 for (int i = 0; i < 10; i++) { if (i % 2 == 0) { food.set("锅包肉", "酸甜可口"); } else { food.set("桑拿滑肉片", "锤儿很喜欢吃"); } } }}/** * 食物:生产者和消费者共享的数据 * * @author Chocus * */class Food { /** * 名称 */ private String name; /** * 描述 */ private String desc; /** * 定义生产消费标记: * true:可以消费,不能生产; * false:可以生产,不能消费; * 注意:开始时要设置为不能消费!!!! */ private boolean flag = false; /** * 生产食物 */ public synchronized void set(String name, String desc) { // 生产的食物还没有被取走,那么先进入等待状态,停止生产 if (flag) { try { this.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } // 模拟生成食物的过程 this.name = name; try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } this.desc = desc; // 产品生产完毕后通知消费者来消费 flag = true; // notifyAll:唤醒休眠中的其他所有线程 // notify:唤醒休眠中的某一个线程(随机) this.notifyAll(); } /** * 取走食物 */ public synchronized void get() { // 当没有食物的食物不能消费,进入等待状态 if (!flag) { try { this.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } // 模拟取走食物的过程 try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("取走食物" + name + ",食物信息:" + desc); // 取走食物后通知生产者生产食物 flag = false; // notifyAll:唤醒休眠中的其他所有线程 // notify:唤醒休眠中的某一个线程(随机) this.notifyAll(); } public Food(String name, String desc) { this.name = name; this.desc = desc; } public Food() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; }}结果 5:5
取走食物锅包肉,食物信息:酸甜可口取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃取走食物锅包肉,食物信息:酸甜可口取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃取走食物锅包肉,食物信息:酸甜可口取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃取走食物锅包肉,食物信息:酸甜可口取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃取走食物锅包肉,食物信息:酸甜可口取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
现在加大力度,两个生产者,两个消费者
public class ProductCustomerDemo { public static void main(String[] args) { Food food = new Food(); Customers c = new Customers(food); Producter p = new Producter(food); Thread t1 = new Thread(c); Thread t2 = new Thread(p); Thread t3 = new Thread(c); Thread t4 = new Thread(p); t1.start(); t2.start(); t3.start(); t4.start(); }}
结果 10:10
取走食物锅包肉,食物信息:酸甜可口取走食物锅包肉,食物信息:酸甜可口取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃取走食物锅包肉,食物信息:酸甜可口取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃取走食物锅包肉,食物信息:酸甜可口取走食物锅包肉,食物信息:酸甜可口取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃取走食物锅包肉,食物信息:酸甜可口取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃取走食物锅包肉,食物信息:酸甜可口取走食物锅包肉,食物信息:酸甜可口取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃取走食物锅包肉,食物信息:酸甜可口取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃取走食物锅包肉,食物信息:酸甜可口取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
一个生产者,两个消费者
public static void main(String[] args) { Food food = new Food(); Customers c = new Customers(food); Producter p = new Producter(food); Thread t1 = new Thread(c); Thread t2 = new Thread(p); Thread t3 = new Thread(c); t1.start(); t2.start(); t3.start(); }结果 5:5,一个人生产,两个人排队领取食物
取走食物锅包肉,食物信息:酸甜可口取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃取走食物锅包肉,食物信息:酸甜可口取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃取走食物锅包肉,食物信息:酸甜可口取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃取走食物锅包肉,食物信息:酸甜可口取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃取走食物锅包肉,食物信息:酸甜可口取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
两个生产者,一个消费者
public static void main(String[] args) { Food food = new Food(); Customers c = new Customers(food); Producter p = new Producter(food); Thread t1 = new Thread(c); Thread t2 = new Thread(p); Thread t4 = new Thread(p); t1.start(); t2.start(); t4.start(); }
结果 6:4,两个人竞争生产,一个人来领取,可以看出,又出现了线程安全问题,即两个人同时在生成中
取走食物锅包肉,食物信息:酸甜可口取走食物锅包肉,食物信息:酸甜可口取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃取走食物锅包肉,食物信息:酸甜可口取走食物锅包肉,食物信息:酸甜可口取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃取走食物锅包肉,食物信息:酸甜可口取走食物锅包肉,食物信息:酸甜可口
根据上述问题,可以找到解决方案
方案一:即在单双数生产食物过程中加上同步即可,但是此种方案可能出现死锁。。。可能。。。所以推荐第二种方案
/** * 生产者 * * @author Chocus * */class Producter implements Runnable { private Food food; public Producter(Food food) { this.food = food; } @Override public void run() { product(); } private synchronized void product() { // 模拟生成食物,单数一种,双数一种 for (int i = 0; i < 10; i++) { if (i % 2 == 0) { food.set("锅包肉", "酸甜可口"); } else { food.set("桑拿滑肉片", "锤儿很喜欢吃"); } } }}
结果 5:5取走食物锅包肉,食物信息:酸甜可口取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃取走食物锅包肉,食物信息:酸甜可口取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃取走食物锅包肉,食物信息:酸甜可口取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃取走食物锅包肉,食物信息:酸甜可口取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃取走食物锅包肉,食物信息:酸甜可口取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃
方案二:生产者和消费者都只唤醒一个线程,即让一个人来做,做好了通知一个人来领
/** * 食物:生产者和消费者共享的数据 * * @author Chocus * */class Food { /** * 名称 */ private String name; /** * 描述 */ private String desc; /** * 定义生产消费标记: * true:可以消费,不能生产; * false:可以生产,不能消费; * 注意:开始时要设置为不能消费!!!! */ private boolean flag = false; /** * 生产食物 */ public synchronized void set(String name, String desc) { // 生产的食物还没有被取走,那么先进入等待状态,停止生产 if (flag) { try { this.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } // 模拟生成食物的过程 this.name = name; try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } this.desc = desc; // 产品生产完毕后通知消费者来消费 flag = true; // notifyAll:唤醒休眠中的其他所有线程 // notify:唤醒休眠中的某一个线程(随机) this.notify(); } /** * 取走食物 */ public synchronized void get() { // 当没有食物的食物不能消费,进入等待状态 if (!flag) { try { this.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } // 模拟取走食物的过程 try { Thread.sleep(500); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("取走食物" + name + ",食物信息:" + desc); // 取走食物后通知生产者生产食物 flag = false; // notifyAll:唤醒休眠中的其他所有线程 // notify:唤醒休眠中的某一个线程(随机) this.notify(); } public Food(String name, String desc) { this.name = name; this.desc = desc; } public Food() { } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDesc() { return desc; } public void setDesc(String desc) { this.desc = desc; }}
结果:5:5
取走食物锅包肉,食物信息:酸甜可口取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃取走食物锅包肉,食物信息:酸甜可口取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃取走食物锅包肉,食物信息:酸甜可口取走食物锅包肉,食物信息:酸甜可口取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃取走食物锅包肉,食物信息:酸甜可口
根据以上知识总结:
阅读全文
1 0
- 8、生产者与消费者案例
- 多线程案例:生产者与消费者
- 生产者与消费者的案例
- 8、生产者与消费者
- 多线程操作案例---生产者与消费者
- kafka快速入门,生产者与消费者案例
- java多线程之生产者与消费者案例
- 多线程——消费者与生产者案例
- 线程--生产者与消费者的案例
- 生产者和消费者案例
- Java多线程与并发(五)之生产者与消费者案例
- 一个生产者与消费者的简单编程案例
- JavaSE 多线程 线程间通信 生产者与消费者案例
- java生产者与消费者经典案例小demo
- 多线程案例:生产者和消费者
- Java生产者和消费者案例
- java生产者和消费者案例
- 生产者与消费者问题
- 无人化运维离我们有多远?阿里智能化运帷平台深度揭秘
- 什么是分布式系统中的幂等性
- css溢出文本处理的几种方法
- ARP-RARP协议
- 1009. Product of Polynomials (25)
- 8、生产者与消费者案例
- webpack3--plugins大用处
- 机制和策略
- android studio混淆打包 总结 站在别人的肩膀上--看的更远
- 工厂方法模式
- Google word2vec 环境搭建
- Core Animation实战一(认识图层CALayer)
- jni
- 数据湖架构—读书笔记[1]