notify丟失、虛假喚醒


  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條件來判斷。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM