為什么WAIT必須在同步塊中


我們知道java的Object有wait和notify方法,如果要使用wait和notify的話,那么必須在synchronized塊中,否則會拋出IllegalMonitorStateException。但是為什么必須在同步塊中調用呢?直接wait,然后在notify不行嗎?我一直存在這樣的疑問,只到后來查到了Stack Overflow的一個回答,豁然開朗。大概翻譯了下:

假設我們要自定義一個blocking queue,如果沒有使用synchronized的話,我們可能會這樣寫:

class BlockingQueue {
    Queue<String> buffer = new LinkedList<String>();

    public void give(String data) {
        buffer.add(data);
        notify();                   // Since someone may be waiting in take!
    }

    public String take() throws InterruptedException {
        while (buffer.isEmpty())    // 不能用if,因為為了防止虛假喚醒
            wait();
        return buffer.remove();
    }
}

這段代碼可能會導致如下問題:

  1. 一個消費者調用take,發現buffer.isEmpty
  2. 在消費者調用wait之前,由於cpu的調度,消費者線程被掛起,生產者調用give,然后notify
  3. 然后消費者調用wait (注意,由於錯誤的條件判斷,導致wait調用在notify之后,這是關鍵)
  4. 如果很不幸的話,生產者產生了一條消息后就不再生產消息了,那么消費者就會一直掛起,無法消費,造成死鎖。

解決這個問題的方法就是:總是讓give/notify和take/wait為原子操作。

也就是說wait/notify是線程之間的通信,他們存在競態,我們必須保證在滿足條件的情況下才進行wait。換句話說,如果不加鎖的話,那么wait被調用的時候可能wait的條件已經不滿足了(如上述)。由於錯誤的條件下進行了wait,那么就有可能永遠不會被notify到,所以我們需要強制wait/notify在synchronized中

最后附上Stack Overflow的原文鏈接:
https://stackoverflow.com/questions/2779484/why-must-wait-always-be-in-synchronized-block


免責聲明!

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



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