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

取走食物锅包肉,食物信息:酸甜可口取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃取走食物锅包肉,食物信息:酸甜可口取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃取走食物锅包肉,食物信息:酸甜可口取走食物锅包肉,食物信息:酸甜可口取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃取走食物桑拿滑肉片,食物信息:锤儿很喜欢吃取走食物锅包肉,食物信息:酸甜可口

根据以上知识总结: