天天看點

消費者和生産者模型的簡單實作

問題描述:假設倉庫中隻能存放一件産品,生産者将生産的産品放入倉庫,然後消費者取出來使用。

分析:這是一個線程同步問題,生産者和消費者共享同一個資源,并且生産者和消費者之間互相依賴、互為條件。對于生産者,沒有生産産品之前,要通知消費者等待。在生産了一個産品之後,停止生産,通知消費者來消費。對于消費者,在消費一個産品之後,通停止消費。并且要通知生産者去生産。

因為這裡面涉及到生産者線程和消費者線程之間的通信,是以隻有 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
           

測試結果如下:

消費者和生産者模型的簡單實作

繼續閱讀