Java中的wait/notify/notifyAll

方法

java.lang.Objectjava

public final native void wait() throws InterruptedException;
public final native void wait(long millis, int nanos) throws InterruptedException;
public final void wait(long millis) throws InterruptedException {
    wait(millis, 0);
}

public final native void notify();
public final native void notifyAll();

wait():使調用該方法的線程釋放鎖,從運行狀態退出,進入等待隊列,直到被喚醒。spa

wait(long timeout):等待一段時間是否有線程喚醒鎖,若是沒有,超時自動喚醒。線程

wait(long timeout, int nanos):等待喚醒時間納秒級別。code

notify():隨機喚醒等待隊列中的等待同一個鎖的一個線程,使這個線程退出等待隊列,進入可運行狀態。對象

notifyAll():喚醒全部等待一樣鎖的全部線程,從等待隊列中退出,進入可運行狀態。隊列

注意點

  1. 在調用wait或者notify以前,必須得到該對象的對象鎖,即,只能在同步方法中調用;
  2. 執行完wait以後釋放對象鎖,因此其餘線程能夠得到執行機會,才能喚醒;
  3. 執行notify以後,不會當即退出讓wait的線程執行,必需要先把同步塊中的程序執行完,退出同步塊,纔會釋放鎖,讓等待線程執行;
  4. notify每次通知一個線程,屢次調用通知線程數增長,可將wait線程所有喚醒。

原理

每一個對象都有個monitor,初始是0,執行完synchronized值就是1。ip

wait/notify須要在得到monitor的線程中才能夠執行。資源

因此,wait/notify須要在synchronized中執行同步

其中,wait又會釋放掉鎖,破壞掉同步。it

跟synchronized關係

synchronized代碼塊生成的字節碼,被monitorentermonitorexit包圍,持有對象的monitor;

線程執行wait/notify方法時,必須持有對象的monitor;

因此,wait/notify方法在synchronized同步塊中執行,就持有了對象的鎖。

互斥和協同

Java語言的同步機制在底層實現上只有兩種方式:互斥和協同。

互斥:即synchronized內置鎖。

協同:即內置條件隊列,wait/notify/notifyAll。

條件隊列中是處於等待狀態的線程,等待特定條件爲真。每一個Java對象均可以做爲一個鎖,一樣每一個Java對象均可以做爲一個條件隊列。經過wait/notify/notifyAll來操做條件隊列。

能夠理解爲:有一個隊列,o.wait()就push進去,o.notify()就pull出來。

要調用條件隊列的任何一個方法,都必需要得到對象上的鎖。

線程是用來工做的,不該該處於等待狀態,處於等待狀態的條件隊列中的線程,必定是執行不下去的。

在while中等待

while(condition is not true) {
  lock.wait()
}

解釋:兩個消費者線程c1和c2,邏輯都是,判斷資源是否爲空,是就wait,否就消費一個;某個時刻,兩個線程都進入等待隊列,而後生產者生產了一個資源,並執行notifyAll,喚醒c1和c2都進入鎖池,c1先獲取鎖,執行完消費掉資源,而後釋放鎖,此時,若是c2得到鎖,若是是if邏輯,那麼就會進入消費代碼,可是資源已經被c1消費掉了,可能拋出異常。若是是while邏輯,則不會進入消費代碼,而是繼續等待。

在通常狀況下,總應該調用notifyAll喚醒全部須要被喚醒的線程。可能會喚醒其餘一些線程,但這不影響程序的正確性,這些線程醒來以後,會檢查他們正在等待的條件(循環檢測),若是發現條件不知足,就會繼續等待

clipboard.png

顯示鎖和顯示條件隊列

顯示鎖:Lock,對應內置鎖synchronized

顯示條件隊列:Condition,對應內置條件隊列,對應方法是await, signal, signalAll

問題

  1. notifyAll喚醒全部線程,但不是全部線程都能執行,必需要等待對象鎖被釋放,獲取鎖以後才能執行。能夠說,notifyAll讓線程進入鎖池。
相關文章
相關標籤/搜索