2011-03-02 111 views
2

我在刷新我關於Java併發的記憶,並且在流行的生產者消費者問題上大顯身手。如果有一個生產者和一個消費者,我已經實現了下面的代碼,它能夠正常工作。但是,如果有多個生產者/消費者,它不能正確運行。我不明白爲什麼Java中的多個生產者和消費者問題(無BlockingQueue)

public class ProducerConsumer { 

    static Monitor monitor; 

    public ProducerConsumer(int maxSize) 
    { 
     monitor = new Monitor(maxSize); 
     new Producer().start(); 
     new Producer().start(); 
     new Consumer().start(); 
     new Consumer().start(); 
    } 

    class Producer extends Thread{ 

     @Override 
     public void run() { 
      while(true) 
      { 
       try { 
        monitor.insert(); 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 
      } 
     } 
    } 

    class Consumer extends Thread{ 

     @Override 
     public void run() { 
      while(true) 
      { 
       try { 
        monitor.remove(); 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 
      } 
     } 
    } 

    class Monitor { 

      int n; 
      int maxSize; 

     public Monitor(int maxSize) 
     { 
      n=0; 
      this.maxSize = maxSize; 
     } 

     synchronized void insert() throws InterruptedException 
     { 
      if(n==maxSize) 
       wait(); 
      System.out.println("Producer: "+n++); 
      if(n==1) 
       notifyAll(); 
     } 

     synchronized void remove() throws InterruptedException 
     { 
      if(n==0) 
       wait(); 
      System.out.println("Consumer: "+n--); 
      if(n==maxSize-1) 
       notifyAll(); 
     } 
    } 

    public static void main(String[] args) { 
     ProducerConsumer pc = new ProducerConsumer(100); 

    } 
} 

回答

8

wait()應始終以下列方式使用:

while(condition not met) 
    wait(); 

否則某些線程可以醒來,繼續當病情依然沒有得到滿足。有這種情況的兩個可能的原因:

  1. 當你調用notifyAll(),你醒了所有等待的線程,使他們中的一些可能是太晚了,當條件爲假試(即當有限的資源已被耗盡其他線程)。
  2. 虛假喚醒有可能(即線程被喚醒而沒有相應的notify)。

如果你真的需要喚醒只有一個線程,你可以使用notify()而不是notifyAll()。它消除了第一個問題,但仍無法保護您免受虛假喚醒,因此仍需要while

+0

我第二(墊+ 1) – ant 2011-03-02 14:54:57

+0

謝謝你,工作。但是,我認爲synchronized方法不允許任何線程輸入它,如果一個線程已經在它。我對導致這種行爲的這個概念有什麼誤解? – 3ashmawy 2011-03-02 15:03:37

+1

@ 3ashmawy:是的,但是當線程調用wait()時,它釋放鎖。之後'notifyAll()'被喚醒的線程排隊後再次獲得鎖,並在wait()後繼續。顯然,該隊列中的第一個線程會改變狀態,最後一個線程會發現條件不再被滿足。 – axtavt 2011-03-02 15:10:26