2014-08-31 46 views
1

我有一個線程啓動另一個線程執行一個動作,它會導致事件在運行後觸發。我需要在第一個線程中捕獲該事件(通過事件偵聽器)並繼續其餘的工作。我的問題是,當第一個線程正在等待事件監聽器調用notify()時,它是否會釋放監視器?如果不是,我該如何設計這個算法?我是否正確使用wait()notify()方法以及正確的鎖定(thread1)?在同步塊中使用wait()方法時,JVM會在等待notify()時釋放監視器嗎?

這是怎樣的代碼看起來像:

public class Thread1 { 
    public void run() { 
     Thread thread1 = Thread.currentThread(); 
     EventListener listener = new EventListener(thread1); 
     Performer performer = new Performer(); 
     performer.addOnPerformedListener(listener); 

     synchronized(thread1) { 
      performer.run(); // Launches thread 2 
      thread1.wait(); 
     } 
     ... 
    } 

    public class EventListener implements Performer.OnPerformedListener { 
     private Thread thread; 

     public EventListener(Thread thread) { 
      this.thread = thread; 
     } 

     @Override 
     public void onPerformed() { 
      synchronized (thread) { 
       thread.notify(); 
      } 
     } 
    } 
} 

回答

0

你在做什麼的基本思想是正確的。作爲Object#wait()方法狀態的Javadoc,會釋放出您獲得使用​​語句在等待監視器:

當前線程必須擁有該對象的監視器。該線程釋放這個監視器的 所有權並等待另一個線程通知 線程在等待這個對象監視器上醒來

但是,爲什麼它不能保證你一定要在任何情況下正常工作的理由實現它,原因是wait調用也可以通過「中斷和虛假喚醒」喚醒。(這將在99%的情況正常運行作爲當前已實現,這使得它更加棘手,因爲這給人一種錯覺,以爲它會一直工作)

正如Javadoc:

正如一個參數的版本,中斷和虛假喚醒是 可能的,而且這種方法應該始終在循環中使用:

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

所以你還需要有些東西表示「執行」的條件 - 最簡單的方法是創建一個名爲performed的域boolean,您在onPerformed方法中調用notify之前設置爲true

該標誌的值將正確地thread1因爲thread2釋放監視器可以看出thread1獲取相同的顯示器之前,並且該建立之前發生的關係如在Java內存模型規範描述。

0

這接近。儘管(在表演者中)沒有剩下的代碼很難說有多接近。

首先,我建議不要在「performer.run()」調用上保持線程鎖定 - 如果可行的話。保持關鍵部分(保持鎖執行的代碼)儘可能小是「最佳實踐」。其次,我建議在執行者中使用條件標誌(如果我正確理解代碼)並在調用wait()之前執行關鍵部分內的檢查 - 以便錯過的通知不會導致錯過條件。

這裏是僞代碼:

THREAD1 
* Initialize THREAD2 
* Start THREAD2 
* synchronized (THREAD2) { while (THREAD2.conditionNotMet()) { THREAD2.wait(); } } 

THREAD2 
* Initialize "condition not met" 
* Run 
* On condition met, set "condition met", then synchronized (THREAD2) { THREAD2.notifyAll(); } 

此外,如果可能會出現病情多次,等待只希望繼續就新出現的條件碼,可試用序列號,而不是隻是一個「爲條件符合「(例如if (myLastOccurrenceNumber < THREAD2.getLastOccurenceNumber) { MET-CONDITION) } else { WAIT })。

+0

下面是一個javadoc參考,也可以幫助:http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html#wait() – ash 2014-08-31 04:06:48

+0

感謝您的答案。我首先包含了一個條件標誌,但是之後根據在執行操作之前條件檢查也會發生並且該標誌設置爲true的原因將其刪除。但是,我發現它有一些安全價值。 – exbuddha 2014-08-31 16:45:22

+0

我不會認爲它是「安全」。相反,請考慮所有可能的操作順序。我使用的方法是遍歷代碼,一次一個部分,並考慮如果代碼停在那裏會發生什麼,而其他競爭線程同時執行競爭代碼。例如,啓動另一個線程然後等待通知,如果另一個線程在第一次進入等待呼叫之前通知會發生什麼情況?錯過了通知。對第二個線程的整個操作的鎖定太粗糙,甚至引發了另一個線程的問題。 hth – ash 2014-08-31 17:54:37

相關問題