2013-07-01 163 views
8

我想檢查如何等待/通知在java中工作。等待()/通知()同步

代碼:

public class Tester { 
    public static void main(String[] args) { 
     MyRunnable r = new MyRunnable(); 
     Thread t = new Thread(r); 
     t.start(); 
     synchronized (t) { 
      try { 
       System.out.println("wating for t to complete"); 
       t.wait(); 
       System.out.println("wait over"); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 
} 

class MyRunnable implements Runnable { 
    public void run() { 
     System.out.println("entering run method"); 
     synchronized (this) { 
      System.out.println("entering syncronised block"); 
      notify(); 
      try { 
       Thread.currentThread().sleep(1000); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
      System.out.println("leaving syncronized block"); 
     } 
     System.out.println("leaving run method"); 
    } 
} 

輸出返回

wating for t to complete 
entering run method 
entering syncronised block 
//sleep called 
leaving syncronized block 
leaving run method 
wait over 

我所期待的通知()時執行的等待將超過& System.out.println("wait over");將得到打印。但它似乎只在t完成其run()時才被打印出來。

+5

你沒有在同一個對象上同步 – MadProgrammer

+0

MyRunnable.this == r!= t –

+1

@ raul8編輯問題並粘貼到答案中使正確答案無效。最好再增加一個問題。 –

回答

9

對象監視器鎖需要進行相同的鎖單參考...

在你的榜樣,你是waitingThread的實例,但使用notifyRunnable。相反,你應該使用一個單一的,共同的鎖定對象...例如

public class Tester { 

    public static final Object LOCK = new Object(); 

    public static void main(String[] args) { 
     MyRunnable r = new MyRunnable(); 
     Thread t = new Thread(r); 
     t.start(); 
     synchronized (LOCK) { 
      try { 
       System.out.println("wating for t to complete"); 
       LOCK.wait(); 
       System.out.println("wait over"); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 

    public static class MyRunnable implements Runnable { 

     public void run() { 
      System.out.println("entering run method"); 
      synchronized (LOCK) { 
       System.out.println("entering syncronised block"); 
       LOCK.notify(); 
       try { 
        Thread.currentThread().sleep(1000); 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 
       System.out.println("leaving syncronized block"); 
      } 
      System.out.println("leaving run method"); 
     } 
    } 
} 

輸出...

wating for t to complete 
entering run method 
entering syncronised block 
leaving syncronized block 
wait over 
leaving run method 

wait overleaving run method可以改變取決於線程調度位置。

您可以嘗試將​​區塊的睡眠偏出。這將釋放監視器鎖定,允許(直到鎖被釋放,因爲它無法啓動)wait部分繼續運行

public static class MyRunnable implements Runnable { 

     public void run() { 
      System.out.println("entering run method"); 
      synchronized (LOCK) { 
       System.out.println("entering syncronised block"); 
       LOCK.notify(); 
       System.out.println("leaving syncronized block"); 
      } 
      try { 
       Thread.currentThread().sleep(1000); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
      System.out.println("leaving run method"); 
     } 
    } 
+0

謝謝...但我試過你的代碼..但它仍然無法正常工作。有問題的代碼更新 – reiley

+0

似乎工作好吧我。你在期待什麼? – MadProgrammer

+0

FYI''Thread.sleep''(兩種形式)都是一種靜態方法,它總是使調用線程進入休眠狀態;因此''Thread.currentThread()。sleep(1000)''在語義上是多餘的,可能會產生誤導(例如調用t.sleep(1000)''會使調用線程進入睡眠狀態,而不是t)。 – kbolino

5

回答到更新的代碼:

Thread.sleep()的javadoc:

使系統定時器 和調度程序的精度和準確度符合 指定的毫秒數,使當前正在執行的線程進入休眠狀態(暫時停止執行)。 線程不會丟失任何顯示器的所有權

如果在同步塊內部調用Thread.sleep,其他線程將無法進入同步塊。您不應該在同步塊中執行耗時的任務來避免這種情況。

1

注意(正如其他人指出的那樣),您必須在兩個線程中使用相同的對象進行鎖定/同步。

如果您希望主線程在調用notify後立即繼續,您必須暫時放棄鎖定。否則wait只有在輔助線程離開​​塊後纔會被調用。在長時間運行的計算中保持鎖定永遠不是一個好主意!

一種方法如何實現是上了鎖的,而不是sleep使用wait(int),因爲wait釋放同步鎖暫時:

public class Tester { 
    private static final Object lock = new Object(); 

    public static void main(String[] args) { 
     Thread t = new Thread(new MyRunnable()); 
     t.start(); 
     synchronized (lock) { 
      try { 
       System.out.println("wating for t to complete"); 
       lock.wait(); 
       System.out.println("wait over"); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
     } 
    } 

    static class MyRunnable implements Runnable { 
     public void run() { 
      System.out.println("entering run method"); 
      synchronized (lock) { 
       System.out.println("entering syncronised block"); 
       lock.notify(); 
       try { 
        lock.wait(1000); // relinquish the lock temporarily 
       } catch (InterruptedException ex) { 
        System.out.println("got interrupted"); 
       } 
       System.out.println("leaving syncronized block"); 
      } 
      System.out.println("leaving run method"); 
     } 
    } 
} 

然而,使用這些低級原可以很容易出錯,我不鼓勵使用它們。相反,我建議你爲此使用Java的高級基元。例如,你可以使用CountDownLatch,它可以讓一個線程等待,直到其他線程計數下降到零:你

import java.util.concurrent.*; 

public class TesterC { 
    private static final CountDownLatch latch = new CountDownLatch(1); 

    public static void main(String[] args) { 
     Thread t = new Thread(new MyRunnable()); 
     t.start(); 

     System.out.println("wating for t to complete"); 
     try { 
      latch.await(); 
     } catch (InterruptedException e) { 
      e.printStackTrace(); 
     } 
     System.out.println("wait over"); 
    } 

    static class MyRunnable implements Runnable { 
     public void run() { 
      System.out.println("entering run method"); 
      try { 
       latch.countDown(); 
       Thread.sleep(1000); 
      } catch (InterruptedException ex) { 
       System.out.println("got interrupted"); 
      } 
      System.out.println("leaving run method"); 
     } 
    } 
} 

在這裏,您不必什麼同步,鎖存器做的一切。還有很多其他可以使用的基元 - 信號燈,交換器,線程安全隊列等。瀏覽器包中包含java.util.concurrent包。

也許更好的解決方案是使用更高層次的API,如Akka規定。在那裏你可以使用ActorsSoftware transactional memory,它可以很容易地編寫,並且可以避免大部分併發問題。