2014-02-12 43 views
3

我知道,我們使用這個成語等待通知辦理虛假喚醒:而(條件){的Object.wait()}成語

synchronized (obj) { 
    while(somecond) 
     obj.wait(); 
} 

如果虛假喚醒出現的時候,我們就檢查狀態並返回到等待狀態。

但是,審時度勢:

  1. 我們開始等待,並obj.wait()釋放鎖定OBJ。
  2. 等待線程虛假由OS
  3. 通知我們返回檢查條件(與obj鎖釋放因等)
  4. obj.notify()就在那一刻被調用。

是的,條件檢查是非常快速的,我們可以在條件檢查中,而不是在obj.wait()中的機率可以忽略不計。在這種情況下,我們可以鬆動obj.notify()電話。

我誤解了一些東西,或者我們真的可以使用這種模式鬆開通知?

回答

8

另一個線程需要obj上的鎖才能撥打obj.notify()。如果你的線程在while循環中沒有等待,它就不能擁有它,因爲你的線程還需要obj上的鎖定在while循環中。

+0

但是,不是調用obj.wait()釋放對obj的鎖嗎?或者obj再次被鎖定,因爲我們得到虛假喚醒,請等待並移動到條件檢查? – Oroboros102

+3

它釋放它,但爲了能夠執行wait()調用之後的操作,它需要重新獲取'obj'上的鎖。同步塊內的每條指令只有在線程持有鎖的情況下才能執行。 –

1

調用obj.wait()將不會返回,直到調用obj.notify()。但是,如果另一個線程也在等待並且系統決定通知該線程,您可能無法響應obj.notify()。如果你想避免這種情況,你可以使用obj.notifyAll()。如果只有一個線程正在等待,則不能使用此模式丟失通知。

請注意,另一個線程不能調用obj.notify(),除非它擁有鎖定。如果此線程正忙於檢查條件,則它具有鎖定,而另一線程無法發出通知。​​塊對操作至關重要。

1

在你目前的情況下,線程A被評估的條件,並且線程B調用notify使得線程A錯過了notify呼叫

這種情況是不可能的notify,因爲被稱爲它必須擁有線程A在同步塊中使用的鎖定 - 只有一個線程可以立即擁有該鎖定。有關更多詳細信息,請參見javadoc on notify

1

應該對我們正在檢查的狀態進行修改,而obj上的鎖定由之後的任何呼叫obj.notify()保存。所以,假設我們正在檢查狀態,我們也持有obj的鎖。

如果我們得到一個虛假的喚醒,並且狀態沒有改變,沒有人應該叫obj.notify()。如果狀態發生了變化,我們錯過了obj.notify(),這並不重要:對於所有人的意圖,虛擬喚醒和通過調用obj.notifiy()喚醒現在具有相同的效果。

教訓是,我們正在檢查的狀態只應該改變,而任何改變狀態的人都會鎖定我們正在等待的對象。

1

由於大部分的答案堅持情景是不可能的它的價值來調整:

它總是可以有notify呼叫沒有匹配wait荷蘭國際集團線程。當通知線程調用notify之前其他線程甚至輸入了整個​​塊時,可能會發生這種情況。在任何線程輸入​​塊到wait並且wait-notify機制將不會計數這些數據之前,甚至可能會多次調用notify方法。

因此,您必須處理您錯過了notify的情況,例如,在撥打wait之前檢查​​區塊內的條件。但通過這樣做,您可以添加處理和重置條件的可能性,而匹配的notify確實處於掛起狀態。

因此,你必須隨時瞭解,要麼

  • 你可能已經錯過了一個或多個notify調用
  • 您可能會收到一個過時的notify

這就是可能性爲什麼正確處理loop like like

synchronized(obj) { 
    while(somecond) 
     obj.wait(); 
} 

從應用程序的角度來看,過時的待執行的notify與由JVM/OS生成的虛假喚醒之間沒有區別,沒有關聯notify調用。這就是爲什麼沒有試圖阻止JVM虛假喚醒的原因。這種努力將被浪費,因爲邏輯不會改變。