爲何wait/notify必需要強制要求放在synchronized中

1 爲何wait/notify必需要強制要求放在synchronized中

在平常開發,咱們都知道wait/notify有着很是固定的一套模板,就是下面的樣子,synchronized同步塊包裹着Object.wait()方法,若是不經過同步塊包住的話JVM會拋出IllegalMonitorStateException異常。java

synchronized(lock) {
    while(!condition){
        lock.wait();
    }
}

那麼爲何要限制這麼寫呢?多線程

2 若是Object.wait()/notify不須要同步

假設咱們本身實現了一個BlockingQueue的代碼。
若是Object.wait()/notify不須要同步,那麼咱們的代碼會形以下面這樣。spa

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

    public void give(String data) {
        buffer.add(data);
        notify();                   // 往隊列裏添加的時候notify,由於可能有人在等着take
    }

    public String take() throws InterruptedException {
        while (buffer.isEmpty())    // 用while,防止spurious wakeups(虛假喚醒)
            wait(); // 當buffer是空的時候就等着別人give
        return buffer.remove();
    }
}

若是上面的代碼能夠執行的話,多線程狀況下會出現一種狀況:線程

  1. 當前buffer是空的,這時來了一個take的請求,還沒有執行到wait語句
  2. 同時又來了一個give請求,完整執行完了整個give方法而且發送了notify
  3. 此時take方法才走到wait,由於它錯過了上一個notify,因此會在明明buffer不空的狀況下掛起線程,take方法掛起。假如再沒有人調用過give方法了,在業務上的表現就會是這個take線程永遠也取不到buffer中的內容

image.png

3 爲何要在JVM層面拋異常

由於你只要用notify,那就是爲了在多線程環境下同步,notify/wait機制自己就是爲了多線程的同步而存在的,那就只能配套synchronized,因此爲了防止上面狀況的發生,就直接強制拋異常來限制開發的代碼模式了。code

4 若是沒有wait/notify

試想一種場景,若是沒有wait/notify的掛起喚醒機制,該如何實現BlockingQueueblog

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

    public void give(String data) {
        buffer.add(data);
    }

    public String take() throws InterruptedException {
        while(buffer.isEmpty) {
            sleep(10);
        }
        return buffer.remove();
    }
}

若是沒有wait/notify,那麼在take的時候只能經過while循環不停輪詢判斷buffer是否爲空來實時獲取buffer的最新狀態,那麼勢必會形成兩種狀況:隊列

  1. sleep時間太短,那麼線程將一直不行循環搶佔CPU,形成CPU打滿
  2. sleep時間過長,那麼將會影響take的時效性

綜上,針對於BlockingQueue這樣的場景,同步塊 + wait/notify 或者 lock + signal/await 就是標配開發

相關文章
相關標籤/搜索