2016-08-19 58 views
4

所以我一直在一個簡單的等待/在Java中,由於某種原因我一直沒能得到它的正常運行通知的例子。如果有人能夠看到可能是什麼問題,將非常感謝!通知()和notifyAll的()沒有在我的Java代碼工作

class producer implements Runnable { 
    StringBuffer sb; 
    producer() { 
     sb=new StringBuffer(""); 
    } 

    public void run() { 
     synchronized(sb) { 
      for(int i = 0; i < 10; i++) { 
       try { 
        sb.append(i+" "); 
        System.out.println("Appending ... "); 
       } catch (Exception e) {} 
      } 
      sb.notify(); 
     } 
    } 
} 

class consumer implements Runnable { 
    producer p; 
    consumer(producer pp) { 
     this.p = pp; 
    } 

    public void run() { 
     System.out.println("Rreached"); 
     synchronized(p.sb) { 
      try { 
       p.sb.wait(); 
      } catch (Exception e) {} 
      System.out.println(p.sb); 
     } 
    } 
} 

class Thread_Comunication { 
    public static void main (String [] args) { 
     producer p = new producer(); 
     consumer c = new consumer(p); 

     Thread t1 = new Thread(p); 
     Thread t2 = new Thread(c); 
     t1.start(); 
     t2.start(); 
    } 
} 

輸出:

Appending ... 
Rreached // randome Position 
Appending ... 
Appending ... 
Appending ... 
Appending ... 
Appending ... 
Appending ... 
Appending ... 
Appending ... 
Appending ... 

所以出於某種原因線程t1沒有醒t2還是我失去了別的東西完全?

+1

至少把'e.printStackTrace();'在'catch'塊,否則你永遠不會知道,如果發生異常。 – Jesper

+1

注意:Java語言約定是類名以大寫字母開頭(因此'producer'不是一個好的類名)。在類,方法或包名稱中不使用下劃線,只用於全部爲大寫的常量名稱。類具有像「ThreadCommunication」這樣的名稱,而不是「Tread_Comunication」。還建議賦予變量有意義的名稱,而不是'p'和'pp'。 – RealSkeptic

+0

關閉投票人:發佈的代碼確實重現了問題(至少儘可能地多,因爲代碼取決於可能或可能不會發生在調度程序的判斷的競爭條件)。在發佈的代碼和顯示的輸出之間,找出這裏發生的事情是很實際的。 –

回答

3

通知除非另一個線程在等待什麼都不做。你的代碼完全依賴於通知(它需要一個條件變量),並依賴於生產者之前運行的消費者才能工作。

根據你的輸出碰巧生產者首先運行;它將在消費者有機會運行之前全部執行。 (對於消費者來說,它需要獲取生產者所持有的sb上的鎖。)生產者調用notify,但沒有線程在等待,所以它沒有效果。然後消費者等待,並沒有通知,所以它無限期地掛起。

如果消費者先運行,則代碼將正常結束。

避免編寫代碼依賴於一個線程發生在另一個之前運行,因爲你沒有什麼先執行控制。當您等待時,您需要在測試條件的循環內執行此操作。其中一個原因是,如果在線程開始等待之前設置了條件,則線程可以知道不會等待。

更改代碼以使用條件:在Java中

import java.io.*; 

class producer implements Runnable { 
    StringBuffer sb; 
    boolean done = false; 
    producer() { 
     sb=new StringBuffer(""); 
    } 

    public void run() { 

     synchronized(sb) { 

      for(int i=0;i<10;i++) { 
       try { 
        sb.append(i+" "); 
        System.out.println("Appending ... "); 
       } catch (Exception e) {} 
      } 
      sb.notify(); 
      done = true; 
     } 


    } 
} 

class consumer implements Runnable { 
    producer p; 
    consumer(producer pp) { 
     this.p=pp; 
    } 

    public void run() { 
     System.out.println("Rreached"); 
     synchronized(p.sb) { 
      try { 
       while (!p.done) { 
       p.sb.wait(); 
       } 
      } catch (Exception e) {} 

      System.out.println(p.sb); 
     } 
    } 
} 


public class Communication { 
    public static void main (String [] args) throws Exception { 
     producer p= new producer(); 
     consumer c= new consumer(p); 

     Thread t1= new Thread(p); 
     Thread t2= new Thread(c); 
     t2.start(); 
     t1.start(); 
    } 
} 
1

所以我一直在一個簡單的等待/通知例如出於某種原因,我一直沒能得到它的正常運行。

您的代碼存在的問題是notify()未被保留。如果producer輸入​​塊第一個,則consumer將不能輸入它並轉到wait,直到producer退出循環並結束。由於所有notify調用發生裏面的​​塊,到consumer到達wait()時,producer已完成並不再呼叫notify。這意味着consumer已掛起。

即使你開始consumer第一,你仍然有可能導致producer進入其​​塊第一的競爭條件 - 因爲consumer調用System.out.println()這需要時間,這是尤其如此。雖然不是「修復」,如果你的​​呼叫前減慢producerThread.sleep(100),你應該看到,現在的工作,因爲consumer到達其wait()之前producer鎖定它。

有幾種方法可以讓你正確地解決這個問題。通常我們使用wait/notify consumer應該檢查的另一個變量。在你的情況下,這可能是sb變量本身,因爲它受到保護。所以consumer可以這樣做:

synchronized (p.sb) { 
    try { 
     // do we need to wait to see if the producer added stuff? 
     if (p.sb.length() == 0) { 
      p.sb.wait(); 
     } 
    } catch (InterruptedException e) { 
     // this is always a good pattern to preserve the interrupt flag 
     Thread.currentThread().interrupt(); 
     return; 
    } 
} 
相關問題