多線程之虛假喚醒


 今天看JDK文檔中的Object.wait()方法,有一段提到:

 對於某一個參數的版本,實現中斷和虛假喚醒是可能的,而且此方法應始終在循環中使用:

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

大致意思就是,wait()方法需要在循環中使用,以避免實現虛假喚醒。那什么是虛假喚醒,話不多說,貼一段代碼來看:

     -----------------------------------------------------代碼分隔線-------------------------------------------------------

Thread-1:
while(true) {
    obj = queue.get();  //2
    // 3
}

 

Thread-2:
synchronized(lock) {

    // 代碼一
    while(queue.isEmpty()) {
          lock.wait();
    // 4
    }
    obj = queue.get(); // 5

    // 代碼二(可能導致虛假喚醒)
    lock.wait();
    // 4
    obj = queue.get(); // 5
}

 

Thread-3:
synchronized(lock) {
    queue.add(obj);
    lock.notify(); // 1
}

     -----------------------------------------------------代碼分隔線-------------------------------------------------------

從上面的偽代碼看到:

一、有三個線程,對同一數據進行訪問

二、線程1未對數據進行安全訪問

三、初始狀態為:queue.size()==0,代碼執行順序為: 1-->2-->3-->4-->5

四、線程2的目的是當queue為空時等待,不為空時取出數據進行處理

五、線程2的代碼一、代碼二只能使用其中一種方式。

 

結論:當線程2使用代碼一時沒有問題,當使用代碼二時,此時線程被喚醒,但仍然取不到數據,這就是虛假喚醒。

虛假喚醒發生的條件為:

1、當一個數據存在三個及以上的線程競爭訪問時(必要條件)

2、至少有一個線程沒有對數據進行加鎖訪問(充分條件,使得虛假喚醒發生可能)

當滿足兩個條件才可能發生虛假喚醒。僅僅是可能,如果代碼執行順序為:1-->4-->5-->2-->3,線程二可以取到數據,也就不存在虛假喚醒。

 

解決虛假喚醒(以下任意一種方式即可):

1、所有的線程訪問數據時都加鎖(線程一就沒有加鎖)

2、在循環中等待(線程2中的代碼一)

 

 


免責聲明!

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



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