2014-12-19 60 views
2

這是在其中,我希望得到如下輸出一個消費者 - 生產者問題:
認沽:0
得到:0
認沽:1
得到:1
.. ..等等。
但與此形成對比的是,儘管使用了wait()和notify()方法,但Consumer類仍然多次使用相同的q值,並且生產者類超出了使用者的範圍。我怎樣才能獲得同步輸出?
同步實施:Java的

這是QFixed類:(定義把()和get()方法)

class QFixed{ 
    int n; 
    boolean valueset = false; 

    synchronized int get(){ 
     if(!valueset){ 
      try { 
       wait(); 
      } catch (InterruptedException ex) { 
       System.out.println("interrupted"); 
      } 
     } 
     System.out.println("Got: " +n); 
     valueset = false; 
     notify(); 
     return n; 
    } 

    synchronized void put(int n){ 
     if (valueset){ 
      try { 
       wait(); 
      } catch (InterruptedException ex) { 
       System.out.println("interrupted"); 
      } 
     } 
     this.n = n; 
     valueset = true; 
     System.out.println("Put: "+n); 
     notify(); 
    } 
} 

這ProducerFixed類:

class ProducerFixed implements Runnable{ 
    Q q; 
    Thread t; 
    public volatile boolean flag = true; 
     ProducerFixed(Q q){ 
      this.q = q; 
      t = new Thread(this,"Producer"); 
      t.start(); 
     } 

    @Override 
     public void run(){ 
      int i =0 ; 
      while(flag){ 
       q.put(i++); 
      } 
     } 

    void stop() { 
     flag = false; 
    } 
} 

這ConsumerFixed類:

class ConsumerFixed implements Runnable{ 
    Q q; 
    Thread t; 
    public volatile boolean flag = true; 

     ConsumerFixed(Q q){ 
      this.q = q; 
      t = new Thread(this,"Consumer"); 
      t.start(); 
     } 

    @Override 
     public void run(){ 
      while(flag){ 
       q.get(); 
      } 
     } 

    public void stop() { 
     flag = false; 
    } 
} 

這Producer_Consumer_Fixed類:

public class Producer_Consumer_Fixed { 
    public static void main(String arg[]){ 
     Q q = new Q(); 
     Producer p = new Producer(q); 
     Consumer c = new Consumer(q); 

     try { 
      Thread.sleep(100); 
     } catch (InterruptedException e) { 
      System.out.println("interrupted"); 
     } 

     p.stop(); 
     c.stop(); 
     try{ 
      p.t.join(); 
      c.t.join(); 
     }catch(InterruptedException e){ 
      System.out.println("interrupted"); 
     } 
    } 
} 
+2

請修正您的代碼中的編譯錯誤,您的代碼似乎很好.. – TheLostMind 2014-12-19 11:16:11

+0

如何解決編譯錯誤? – 2014-12-19 11:28:18

+1

您向我們展示的代碼不能是您正在執行的代碼。 '新Q()'< - 您提供的示例中沒有名爲'Q'的類。 – 2014-12-19 11:32:25

回答

4

if (flag) wait你QFixed get和put方法中使用的成語壞了,你應該使用while循環來代替。請參閱the Oracle tutorial on guarded blocks

有一次,我改變了類的名稱以去除「固定」,並在Q類while更換if,像這樣:

class Q { 
    int n; 
    boolean valueset = false; 

    synchronized int get(){ 
     while(!valueset){ 
      try { 
       wait(); 
      } catch (InterruptedException ex) { 
       System.out.println("interrupted"); 
      } 
     } 
     System.out.println("Got: " +n); 
     valueset = false; 
     notify(); 
     return n; 
    } 

    synchronized void put(int n){ 
     while (valueset){ 
      try { 
       wait(); 
      } catch (InterruptedException ex) { 
       System.out.println("interrupted"); 
      } 
     } 
     this.n = n; 
     valueset = true; 
     System.out.println("Put: "+n); 
     notify(); 
    } 
} 

我得到了輸出開始

Put: 0 
Got: 0 
Put: 1 
Got: 1 
Put: 2 
Got: 2 
Put: 3 
Got: 3 
Put: 4 
Got: 4 
Put: 5 
Got: 5 
Put: 6 
Got: 6 
Put: 7 
Got: 7 
Put: 8 
Got: 8 
Put: 9 
Got: 9 
Put: 10 
Got: 10 
... 

每個值都被放置並獲得一次,這是您想要的輸出。

使用while循環有好幾個原因是件好事。

等待線程放棄監視器,一旦喚醒它必須重新獲取監視器,才能繼續退出等待方法。這意味着其他線程可以使用監視器並可能更改同步所保護的數據的狀態。一旦線程重新獲得了監視器,它就需要再次檢查該條件,然後才能知道它是否認爲它實際發生了通知。否則,線程將根據陳舊的信息決定要執行什麼操作。

在涉及三個或更多競爭線程的例子中,這將是一個大問題。但是,對於這個特定情況,我沒有看到有問題的操作順序。

while循環的另一個原因是,僅僅因爲線程退出等待並不一定意味着發生了通知。根據javadoc for Object#wait

線程也可以喚醒而不會被通知,中斷或超時,所謂的虛假喚醒。雖然這在實踐中很少會發生,但應用程序必須通過測試應該引起線程被喚醒的條件來防範它,並且在條件不滿足時繼續等待。換句話說,等待應總是發生在循環中,像這樣的:

synchronized (obj) { 
    while (<condition does not hold>) 
     obj.wait(timeout); 
    ... // Perform action appropriate to condition 
} 

這來自於JVM實現競爭條件;就像文件說的那樣,它應該是一種罕見的現象。但這可能是問題的根源,在沒有得到通知的情況下獲得等待回報可能會產生像您所看到的那樣的多重獲益。