notify丟失:

  假設執行緒A因為某種條件在條件佇列中等待,同時執行緒B因為另外一種條件在同一個條件佇列中等待,也就是說執行緒A/B都被同一個Object.wait()掛起,但是等待的條件不同。

  現在假設執行緒B的執行緒被滿足,執行緒C執行一個notify操作,此時JVM從Object.wait()的多個執行緒(A/B)中隨機挑選一個喚醒,不幸的是喚醒了A。此時A的條件不滿足,於是A繼續掛起。而此時B仍然在傻傻的等待被喚醒的訊號。也就是說本來給B的通知卻被一個無關的執行緒持有了,真正需要通知的執行緒B卻沒有得到通知,而B仍然在等待一個已經發生過的通知。

如果使用notifyall,則能夠避免此問題。notifyall會喚醒所有正在等待的執行緒,執行緒C發出的通知執行緒A同樣能夠收到,但是由於對於A沒用,所以A繼續掛起,而執行緒B也收到了此通知,於是執行緒B正常被喚醒。

  既然notifyall能夠解決單一notify丟失通知的問題,那麼為什麼不總是使用notifyall替換notify呢?

  假設有N個執行緒在條件佇列中等待,呼叫notifyall會喚醒所有執行緒,然後這N個執行緒競爭同一個鎖,最多隻有一個執行緒能夠得到鎖,於是其它執行緒又回到掛起狀態。這意味每一次喚醒操作可能帶來大量的上下文切換(如果N比較大的話),同時有大量的競爭鎖的請求。這對於頻繁的喚醒操作而言效能上可能是一種災難。

  如果說總是隻有一個執行緒被喚醒後能夠拿到鎖,那麼為什麼不使用notify呢?所以某些情況下使用notify的效能是要高於notifyall的。

  如果滿足下面的條件,可以使用單一的notify取代notifyall操作:

  相同的等待者,也就是說等待條件變數的執行緒操作相同,每一個從wait放回後執行相同的邏輯,同時一個條件變數的通知至多隻能喚醒一個執行緒。

  lock上可以存在多個等待佇列,應該可以比較好的解決notify丟失的問題。

   wait最好放在while迴圈中,以避免“虛假喚醒”的情形。即執行緒由於某些特殊情況,不是被notify或者notifyAll所喚醒,所以還需要再次判斷條件是否成立。

  所以wait應該放到while迴圈中,而不是簡單的使用if條件來判斷。