天天看点

消费者和生产者模型的简单实现

问题描述:假设仓库中只能存放一件产品,生产者将生产的产品放入仓库,然后消费者取出来使用。

分析:这是一个线程同步问题,生产者和消费者共享同一个资源,并且生产者和消费者之间相互依赖、互为条件。对于生产者,没有生产产品之前,要通知消费者等待。在生产了一个产品之后,停止生产,通知消费者来消费。对于消费者,在消费一个产品之后,通停止消费。并且要通知生产者去生产。

因为这里面涉及到生产者线程和消费者线程之间的通信,所以只有 synchronized 是不够的。

Java中提供了5个方法来解决线程之间的通信,其中常用的是wait() 和 notify()方法。

final void wait() 表示线程一直等待,直到其他线程通知;
void wait(long timeout) 线程等待指定毫秒级别的参数的时间
final void wait(long timeout, int nanos) 线程等待指定毫秒+微妙的时间
final void notify() 唤醒一个处于等待状态的线程
final void notifyAll() 唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先运行。
           

   注意:它们均是Object类的方法,都只能在同步方法或者同步代码块中使用,否则会抛出异常。

下面是源码部分的书写:

//我们定义一个产品类,并且其中提供了Product类的name和color属性的set和get方法。
//还有一个isProduct属性来代表当前仓库中是否有产品,其实就是一个互斥锁。
//同时,我们不再在生产者线程和消费者线程中写 生产产品的put()方法 和 消费产品的get()方法的详细内容,
//而是直接在Product类中提供,然后在生产者和消费者线程中去调用。
//最后,我们先将put() get()方法用 synchronized关键字 声明为 同步方法 。
//然后用互斥锁 isProduct ,并结合wait() notify()方法,实现互斥操作。

package java_demo.Thread.commu1;
/**
 * @author: lei
 * @data: 2019.10.14 16:45
 * @description: 产品类
 */
public class Product {
   private String name;  //名字 :玉米 馒头
   private String color; //颜色 :黄色 白色
   private boolean isProduct = false;  //默认没有生产
   public String getName() {
      return name;
   }
   public void setName(String name) {
      this.name = name;
   }
   public String getColor() {
      return color;
   }
   public void setColor(String color) {
      this.color = color;
   }
   //生产商品
   public synchronized void put(String name, String color) throws InterruptedException {
      //1.如果有商品了,等待唤醒
      if(isProduct)
         wait();
      //2.生产商品
      this.setName(name);
      //加入sleep之后,进一步观察是否出现 生产玉米白色 的错误现象。
      try {
         Thread.sleep(10);
      } catch (InterruptedException e) {
         e.printStackTrace();
      }
      this.setColor(color);
      System.out.println("生产者生产商品,并且放入仓库中." + name + "---" + color);
      //3.修改isProduct的值
      isProduct = true;
      //4.唤醒消费者
      notify();
   }
   
   //消费商品
   public synchronized void get() throws InterruptedException {
      //1.等待商品
      if(!isProduct)
         wait();
      //2.消费商品
      System.out.println("消费者从仓库取出商品." + this.getName() + "---" + this.getColor());
      //3.修改isProduct的值
      isProduct = false;
      //4.唤醒生产者
      notify();
   }
}//class end
           
//这里是生产者线程,直接调用Product类中的put()方法。采用实现Runnable接口的方式来定义。
//其中,提供了两个构造函数,并且在run()方法的重写中,加入对i的判断,当i为偶数时,生产白色的馒头;i为奇数时,生产黄色的玉米。
package java_demo.Thread.commu1;
/**
 * @author: lei
 * @data: 2019.10.14 16:46
 * @description: 生产者类
 */
public class Producer implements Runnable{
   private Product product;
   public Producer(){
      super();
   }
   public Producer(Product product){
      super();
      this.product = product;
   }
   //馒头(i%2 = 0)和玉米(i%2 = 1)交替生产
   @Override
   public void run() {
      int i = 0;
      while(true){
         //进行生产
         if(i%2 == 0){
            try {
               product.put("馒头","白色");
            } catch (InterruptedException e) {
               e.printStackTrace();
            }
         }
         else{
            try {
               product.put("玉米","黄色");
            } catch (InterruptedException e) {
               e.printStackTrace();
            }
         }
         //改变i的值
         i++;
      }
   }
}//class end
           
//这里是消费者线程,直接调用Product类中的get()方法。采用实现Runnable接口的方式来定义。
package java_demo.Thread.commu1;
/**
 * @author: lei
 * @data: 2019.10.14 16:46
 * @description: 消费者线程
 */
public class Consumer implements Runnable {
   private Product product;
   public Consumer(){
      super();
   }
   public Consumer(Product product) {
      super();
      this.product = product;
   }
   @Override
   public void run() {
      while(true){
         try {
            product.get();
         } catch (InterruptedException e) {
            e.printStackTrace();
         }
      }
   }
}//class end
           
//这是我们的测试类
package java_demo.Thread.commu1;

/**
 * @author: lei
 * @data: 2019.10.14 16:42
 * @description:
 * 业务:
*   1、仓库中只放一件商品
*   2、生产者交替生产玉米和馒头
*   3、生产者和消费者交替工作
 * 设计:
 *  1、产品类 Product(name、color)
 *  2、生产者线程 Producer
 *  3、消费者线程 Consumer
 *  4、测试类 Test
 *
 *  问题1:两个线程之间没有交互
 *  问题2:和商品之间没有关系 解决:提供商品类、生产者交替生产馒头和玉米、消费者消费、测试类中给生产者和消费者传入同一个产品
 *  问题3:出现白色玉米和黄色馒头的错误情况 解决:同步代码块
 *  问题4:生产放到生产者线程中,消费放到消费者线程中,不符合面向对象的封装思想 解决:把生产和消费作为方法放到product类中
 *  问题5:交替生产玉米和馒头 其实就是问题1,但最后才去解决:线程通信 借助方法:wait() notify() 注意只要方法声明中有synchronized 就可以直接调用这些方法
 *
 */
public class Test {
   public static void main(String[] args) {
      //创建两个线程
      //创建产品
      Product p = new Product();
      Thread th1 = new Thread(new Producer(p));
      Thread th2 = new Thread(new Consumer(p));
      //启动线程
      th1.start();
      th2.start();
   }
}//class end
           

测试结果如下:

消费者和生产者模型的简单实现

继续阅读