2013-09-27 49 views
2

我有從ThreadBThreadA發送通知的函數,並等待ThreadA返回ThreadB的通知。交叉同步塊

A之間週期由B通知,然後由B通知A讀取ThreadAThreadB數據,並且當完成通知發送給ThreadB

sendNotifyAndWaitNotify在ThreadB中運行。

在同一個對象上一個一個的同步可能看起來很奇怪,但B.sync.notify();只會在退出同步塊時才發送通知,如果我將所有內容寫入一個塊中,我將等待第一次並且只有在退出同步塊時它纔會執行通知。這是不可接受的。

void sendNotifyAndWaitNotify() { 
    synchronized(B.sync) { 
     B.sync.notify(); 
    } 
    //ThreadA reads ThreadB data 
    synchronized (B.sync) {  
     try { 
      debug("start waiting for notify"); 
      B.sync.wait(); 
     } catch (Exception e) { 
      error(e.toString()); 
     } 
    } 
    debug("reader sleep done"); 
} 

但是這個函數不正確,因爲在ThreadB沒有開始等待的時刻,ThreadA可以讀取數據並通知ThreadB。是否有可能通過雜交同步塊像波紋管以解決問題:

void sendNotifyAndWaitNotify() 
    { 
    synchronized(B.sync) // block #1 
     { 
     synchronized(B.sync) // block #2 
     { 
     B.sync.notify(); 
     } 

    //ThreadA reads ThreadB data 

     try 
     { 
     debug("start waiting for notify"); 
     B.sync.wait(); 
     } catch (Exception e) { 
     error(e.toString()); 
     } 
     debug("reader sleep done"); 
     } 
    } 

我期望的ThreadA將離開同步塊#1和#未2之後將被喚醒。我期望塊#2中的代碼也許第二個塊更適合在不同的同步對象上創建?

我必須使用java 1.4。

+0

你需要使用等待/通知? – maasg

+2

我已經編輯了你的代碼來修正格式,但是似乎有些問題。你可以重寫和編輯你的文字來解釋你想要做什麼嗎? – Gray

+0

Java 5引入了許多有用的併發構造......我相信其中一個將會優雅地解決這個問題。最好在程序員的問題上對設計進行白板。 –

回答

1

您應該始終保持呼叫wait()與環狀條件。

synchronized(B.sync){ 
    while(!myConditionIsSatisfied){ 
     B.sync.wait(); 
    } 
    /* Do critical stuff */ 
} 

如果您的病情已經由線程到達同步塊的時間滿足這種方式,它不會等待任何東西。

守住等待環狀態的另一個原因是wait()被其合約允許醒來虛假。如果發生這種情況,如果您的條件不滿足,您希望將線程發送回wait()

參見:JavaDoc for Object.wait(long)的Object.wait()是調用Object.wait(0)

節選(重點煤礦):

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

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

嘗試以下。使用兩個監視器和布爾標誌來指示線程是否必須等待。這將保護您免受虛假喚醒,並且您不會丟失通知。

你也可以使用一個顯示器,但在我看來這是更好的可讀性。

如果線程A調用監視器上的通知並且線程B尚未處於等待狀態,程序將永遠停止。所以測試一下你是否必須等待。

public class Testnotify { 

    private final Object monitorA = new Object(); 
    private boolean finishedA = false; 

    private final Object monitorB = new Object(); 
    private boolean finishedB = false; 

    private void threadAwaitAndNotify() throws InterruptedException { 
     // do some stuff 

     // notify the other thread that we are finished here 
     synchronized (monitorA) { 
      finishedA = true; 
      System.out.println(Thread.currentThread().getName() 
        + " Notify thread B."); 
      monitorA.notifyAll(); 
     } 

     // now wait for the other thread to finish 
     synchronized (monitorB) { 
      while (!finishedB) { 
       System.out.println(Thread.currentThread().getName() 
         + " Waiting for thread B."); 
       monitorB.wait(); 
      } 
      System.out.println(Thread.currentThread().getName() 
        + " Thread B ready."); 
     } 
    } 

    private void threadBwaitAndNotify() throws InterruptedException { 
     synchronized (monitorA) { 
      while (!finishedA) { 
       System.out.println(Thread.currentThread().getName() 
         + " Waiting for thread A."); 
       monitorA.wait(); 
      } 
      System.out.println(Thread.currentThread().getName() 
        + " Thread A ready."); 
     } 

     synchronized (monitorB) { 
      finishedB = true; 
      System.out.println(Thread.currentThread().getName() 
        + " Notify thread A."); 
      monitorB.notifyAll(); 
     } 
    } 

    public static void main(String[] args) { 
     final Testnotify testnotify = new Testnotify(); 

     final Thread threadA = new Thread(new Runnable() { 

      @Override 
      public void run() { 
       try { 
        testnotify.threadAwaitAndNotify(); 
       } catch (InterruptedException e) { 
        Thread.currentThread().interrupt(); 
       } 
      } 
     }, "A"); 

     final Thread threadB = new Thread(new Runnable() { 

      @Override 
      public void run() { 
       try { 
        testnotify.threadBwaitAndNotify(); 
       } catch (InterruptedException e) { 
        Thread.currentThread().interrupt(); 
       } 
      } 
     }, "B"); 

     threadA.start(); 
     threadB.start(); 

    } 

} 

會給你的輸出:

B Waiting for thread A. 
A Notify thread B. 
A Waiting for thread B. 
B Thread A ready. 
B Notify thread A. 
A Thread B ready.