2016-11-04 55 views
0

我在我的代碼中使用了wait()和notify(),並且在另一個線程上的鎖對象上實際調用了一個等待之前,執行可能會先到達notify()。我在鎖對象中創建了一個「isWaitCalled」標誌,並且如果wait()沒有被調用,就使用它跳過notify()調用。這個標誌「isWaitCalled」是多餘的嗎?在等待其中一個場景之前調用notify()是否可以? 線程A:在調用wait()之前調用鎖對象的notify()是否可以?

synchronized (syncObject) { 
      try { 
       if (!syncObject.isLoadComplete) { 
        syncObject.isWaitCalled = true; 
        syncObject.wait(); 
       } 
      } catch (Exception ex) { 
      }            
    } 

ThreadB:

synchronized (syncObject) { 
      try { 
       if (!syncObject.isLoadComplete) { 
        syncObject.isLoadComplete =true; 
        if (syncObject.isWaitCalled) { 
         syncObject.notify();   
        }     
       } 
      } catch (Exception ex) { 
      }            
    } 
+0

。如果樹倒在森林裏,但沒有一個人聽到它,它發出聲音? – erickson

+0

這就是我的想法。想要確認,並不想推測。 – arun8

回答

3

在沒有線程等待的對象上調用notify()notifyAll()沒有特殊問題。特別是,它不會阻塞,從這個意義上講,isWaitCalled變量沒有任何用處。

但是請注意,通知只對傳遞通知時已經在等待的線程有效。特別是,在notify()之後調用對象的wait()方法的線程將阻塞,直到下一個通知。出於這個原因,標準的等待/通知範例要求線程在等待它們是否需要等待之前進行檢查。

線程會以最簡單的形式檢查某個共享變量的值,以確定是否等待以及是否從等待返回後重新等待。通知線程則負責在發送通知之前適當修改該變量。這或多或少與你所呈現的相反。

0

不知怎麼的,你要知道是否有東西要等待還是不行。否則,您無法知道是否撥打wait。這個你在等待的東西叫做「謂詞」。在你的情況下,isLoadComplete已經是謂詞,所以有另一個跟蹤是否需要等待的標誌是多餘的。

只有在isLoadCompletefalse時,您才需要等待。當且僅當您將isLoadComplete設置爲true時,請致電notify。是什麼讓你覺得你需要比這更多?當沒有線程在等待時,調用notify是無害的。

2

在調用wait()之前調用鎖對象的notify()方法可以嗎?

那麼,這取決於上下文。表面上看,這沒有害處。

但是,如果您的代碼取決於調用wait()看到特定通知的線程,那麼它將不起作用。通知未排隊。如果notify()呼叫發生時沒有任何等待,通知將被丟棄。

但是,您的代碼因其他原因而中斷。

  1. Java等待/通知可能產生虛假通知;即在wait()中的線程可能會被喚醒而沒有特定的notify()notifyAll()。您的代碼假設在線程A中返回wait()後已設置loadComplete。由於虛假喚醒,它不能假設...。

  2. 您假設只有一個線程可能正在等待加載完成。如果不是這樣,那麼一個線程會被喚醒,其餘線程可能會被永久卡住。

  3. 最後,捕捉和丟棄Exception這樣的錯誤有兩點。

    • 您正趕上/擠壓其他任何檢查或未經檢查的異常(除了Error等)。
    • 你忽略了一個檢查的異常,你應該是處理。如果線程A或線程B在不幸的時間得到中斷,那麼事情就會中斷。特別是線程A可能會認爲代碼已經加載時,它沒有。 如果你不打算處理中斷,那麼把它們當作「這種不應該發生」的情況......並且拋出一個AssertionError,或者同樣致命的東西。

我勸你使用標準模式實現了在該java.lang.Object的javadoc提出一個條件變量。使用循環,不要嘗試優化不必要通知的情況。如果您感受到優化的衝動,那麼請嘗試使用其中一個高級併發結構(例如「latch」),而不是實現您自己的解決方案。

如果您獲得「創意」,您可能會陷入困境。即使你的代碼是完美的,你也會爲維護你的代碼的人們造成額外的工作。如果他們遇到了神祕的併發錯誤,他們可能需要從第一原則(重新)分析您的「創造性」解決方案,以確定它是否確實是合理的。

這就是你如何應該實現這一點。正如你所看到的,有代碼比你的解決方案更低,更容易理解:

的ThreadA:

synchronized (syncObject) { 
     try { 
      // The loop deals with spurious wakeups. 
      while (!syncObject.isLoadComplete) { 
       syncObject.wait(); 
      } 
     } catch (InterruptedException ex) { 
      // This is the "easy" way to do it correctly, in the case 
      // where interrupts are not designed for. 
      throw AssertionError("interrupted unexpectedly"); 
     }            
    } 

ThreadB:

synchronized (syncObject) { 
     if (!syncObject.isLoadComplete) { 
      syncObject.isLoadComplete = true; 
      syncObject.notifyAll(); // use `notify()` only if threadA 
            // is guaranteed to be the only waiter. 
     }            
    } 
0

當你發現自己使用

wait()

方法,並且該方法調用不在循環中,y你幾乎肯定會做錯事。

循環的形式應該

while (_whatever_im_waiting_for_to_happen_has_not_happened_) 
    wait(); 
} 
相關問題