天天看點

Java 多線程程式設計之六:線程之間的通信(附源代碼)

Java 多線程程式設計之六:線程之間的通信(附源代碼)

​​源代碼下載下傳​​

       多線程程式設計中,線程之間的通信是一個比較複雜的問題。大家往往搞不懂什麼是競争資源?什麼時候考慮同步?怎麼進行同步?什麼是線程通信?怎麼進行通信?很多朋友面試的時候都遇到過這樣類似的一個程式設計題:給定一個場景,編寫代碼來恰當使用等待、通知和通知所有線程。相信很多朋友對java.lang.Object類的這三個方法都很熟悉,notify、notifyAll、wait,但是真正能運用自如的卻不多。是以面試中挂在上面那個面試題上的朋友還真不在少數。本文列舉了一個經典的生産者、消費者源代碼,以代碼的角度來說明這個問題,并附加示例源代碼,相信讀者看過之後對線程之間的通信互動會有更進一步的認識!

        生産者-消費者模型,堪稱多線程程式中的經典。本源碼中将使用 java.lang.Object 的 wait、notify、notifyAll 來實作這個模型,這才是最重要的。

        開始以前,讓我們先來熟悉一下生産者-消費者模型的遊戲規則:

1、倉滿不能生産;

2、藏空不能消費;

3、消費者消費産品前發現不能滿足此次消費後通知生産者進行生産;

4、生産者生産出産品後通知消費者進行消費。

        好的。開始之前再來回顧一下對象鎖的概念……這是最關鍵的。每個對象都有一個内置鎖。當程式運作到非靜态 synchronized 方法上時,将自動獲得與正在執行代碼類的目前執行個體(即 this 執行個體)有關的鎖。java.lang.Thread.sleep() 方法執行時并不釋放此鎖;java.lang.Object.wait() 方法執行時釋放此鎖。好了,就到這裡吧,說太多了一來顯得作者羅嗦,二來也有侮辱讀者 Java 基礎的嫌疑。開始代碼示範。

生産者-消費者模型-倉庫源代碼

package com.defonds.thread;
/**
 * 
 * 
 * 項目名稱:ThreadApp
 * 類名稱:Godown
 * 類描述:生産者-消費者模型之倉庫
 * 建立人:Defonds
 * 建立時間:2010-1-26 上午10:50:00
 * 修改人:Defonds
 * 修改時間:2010-1-26 上午10:50:00
 * 修改備注:
 * @version 
 *
 */
public class Godown {
  
  private final int max_size = 100;//最大庫存容量
  private int curNum;//現有庫存量
  
  /**
   * 
   * 建立一個新的執行個體 Godown.
   *
   * @param curNum
   */
  public Godown(int curNum){
    this.curNum = curNum;
  }
  
  /**
   * 生産指定數目的産品
   */
  public synchronized void produce(int needNum){
    while(true){
      /**
       * 如果不需要生産,進入等待狀态
       */
      while(this.curNum + needNum > this.max_size){
        System.out.println(Thread.currentThread().getName() + "要生産的産品數量" + needNum + "已經超出剩餘庫存容量" + (this.max_size - this.curNum) +",暫時不能進行生産任務!");
        try {
          this.wait();
        } catch (InterruptedException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
      }
      
      /**
       * 滿足了生産條件,進行産品生産
       */
      this.curNum += needNum;
      System.out.println(Thread.currentThread().getName() + "已經生産了" + needNum + ",現存庫存量是為:" + this.curNum);
      
      /**
       * 喚醒在此對象螢幕上等待的所有線程
       */
      this.notifyAll();
    }
  }
  
  /**
   * 消費指定數目的産品
   */
  public synchronized void consume(int needNum){
    while(true){
      /**
       * 如果不可以消費,進入等待狀态
       */
      while(this.curNum < needNum){
        System.out.println(Thread.currentThread().getName() + "要消費的産品數量" + needNum + "已經超出剩餘庫存量" + this.curNum + ",暫時不能進行消費!");
        try {
          this.wait();
        } catch (InterruptedException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
        }
      }
      
      /**
       * 滿足了消費條件,進行産品消費
       */
      this.curNum -= needNum;
      System.out.println(Thread.currentThread().getName() + "已經消費了" + needNum + ",現存庫存量是為:" + this.curNum);
      
      /**
       * 喚醒在此對象螢幕上等待的所有線程
       */
      this.notifyAll();
    }
  }
}      

生産者-消費者模型-生産者源代碼

package com.defonds.thread;
/**
 * 
 * 
 * 項目名稱:ThreadApp
 * 類名稱:Producer
 * 類描述:生産者-消費者模型之生産者
 * 建立人:Defonds
 * 建立時間:2010-1-26 上午10:45:30
 * 修改人:Defonds
 * 修改時間:2010-1-26 上午10:45:30
 * 修改備注:
 * @version 
 *
 */
public class Producer extends Thread{
  
  private int needNum;//每次要生産産品的數量
  private Godown godown;//倉庫
  
  /**
   * 
   * 建立一個新的執行個體 Producer.
   *
   * @param needNum
   * @param godown
   */
  public Producer(int needNum,Godown godown){
    this.needNum = needNum;
    this.godown = godown;
  }
  
  /**
   * 重寫 java.lang.Thread 的 run 方法
   */
  public void run(){
    this.godown.produce(this.needNum);
  }
}      

生産者-消費者模型-消費者源代碼

package com.defonds.thread;
/**
 * 
 * 
 * 項目名稱:ThreadApp
 * 類名稱:Consumer
 * 類描述:生産者-消費者模型之消費者
 * 建立人:Defonds
 * 建立時間:2010-1-26 上午10:50:48
 * 修改人:Defonds
 * 修改時間:2010-1-26 上午10:50:48
 * 修改備注:
 * @version 
 *
 */
public class Consumer extends Thread{
  
  private int needNum;//每次要消費産品的數量
  private Godown godown;//倉庫
  
  /**
   * 
   * 建立一個新的執行個體 Consumer.
   *
   * @param needNum
   * @param godown
   */
  public Consumer(int needNum,Godown godown){
    this.needNum = needNum;
    this.godown = godown;
  }
  
  /**
   * 重寫 java.lang.Thread 的 run 方法
   */
  public void run(){
    this.godown.consume(this.needNum);
  }
}      

生産者-消費者模型-程式入口源代碼

package com.defonds.thread;
/**
 * 
 * 
 * 項目名稱:ThreadApp
 * 類名稱:ThreadApp
 * 類描述:生産者-消費者模型之程式入口
 * 建立人:Defonds
 * 建立時間:2010-1-26 上午10:51:15
 * 修改人:Defonds
 * 修改時間:2010-1-26 上午10:51:15
 * 修改備注:
 * @version 
 *
 */
public class ThreadApp {
  public static void main(String[] args) {
    
    /**
     * 倉庫初始化
     */
    Godown godown = new Godown(30);
    
    /**
     * 消費者初始化
     */
    Consumer consumer0 = new Consumer(30,godown);
    Consumer consumer1 = new Consumer(20,godown);
    Consumer consumer2 = new Consumer(40,godown);
    
    /**
     * 生産者初始化
     */
    Producer producer0 = new Producer(10,godown);
    Producer producer1 = new Producer(10,godown);
    Producer producer2 = new Producer(10,godown);
    Producer producer3 = new Producer(10,godown);
    Producer producer4 = new Producer(10,godown);
    Producer producer5 = new Producer(10,godown);
    Producer producer6 = new Producer(10,godown);
    Producer producer7 = new Producer(10,godown);
    
    /**
     * 标記每個生産者/消費者
     */
    consumer0.setName("consumer0");
    consumer1.setName("consumer1");
    consumer2.setName("consumer2");
    producer0.setName("producer0");
    producer1.setName("producer1");
    producer2.setName("producer2");
    producer3.setName("producer3");
    producer4.setName("producer4");
    producer5.setName("producer5");
    producer6.setName("producer6");
    producer7.setName("producer7");
    
    /**
     * 開始進行生産-消費
     */
    consumer0.start();
    consumer1.start();
    consumer2.start();
    producer0.start();
    producer1.start();
    producer2.start();
    producer3.start();
    producer4.start();
    producer5.start();
    producer6.start();
    producer7.start();
  }
}