【Object類中的wait()方法和notify()方法】
wait():
public final void wait(long timeout) throws InterruptedException
參數:等待的時間,可選,不填則預設為0。
說明:
1>使線程主動釋放對象鎖,并進入等待狀态,直到它被其他線程通過notify()或notifyAll喚醒或者超過指定的等待時間。
2>在調用wait方法前,線程必須獲得該對象的對象鎖,即:隻能在同步方法或同步代碼塊中調用wait方法,如果目前線程不是鎖的持有者,将抛出一個IllegalMonitorStateException異常(是RuntimeException的子類,故不需要捕獲)。
3>wait方法執行完成後,該線程立即釋放持有的對象鎖(注:不是等到退出synchronized代碼塊後才釋放)。
4>this method should always be used in a loop:
synchronized (obj) {
// 不滿足條件時等待。
while (condition does not hold) {
obj.wait(); // 注:一般會有其它的線程當條件滿足後調用notify方法
}
// 滿足條件時執行execute方法。
execute(); // 注:使用while循環保證了這行代碼始終是在滿足條件下被執行的!
}
舉例:
階段1)A線程執行該代碼塊時,發現不滿足條件condition,則A線程進入等待狀态,并釋放對象鎖,
階段2)B線程執行某些代碼後,滿足了條件condition,然後調用notify()方法并釋放了對象鎖,
階段3)如果此時C線程獲得了該對象鎖,并執行了某些代碼導緻又不滿足條件condition了,當C線程釋放該對象鎖後,
階段4)A線程才獲得了該對象鎖,并執行wait()方法後面的代碼。如果不使用while循環,而是使用if語句來判斷(如下),則會導緻execute()方法在不滿足條件的情況下被執行了!
synchronized (obj) {
if (condition does not hold) {
obj.wait();
}
execute();
}
解析:
執行execute方法的情況:
1)執行代碼塊時,條件已滿足,則不會調用wait方法,直接執行execute方法。
2)執行代碼塊時,條件不滿足,則調用wait方法,等到被喚醒後(注:被喚醒後條件可能發生變化),再去執行execute方法。
使用while循環:保證了在調用wait方法前(階段1)和在被喚醒後(階段4)都去檢查條件是否滿足,如果滿足則執行excute方法,如果不滿足則繼續等待。
使用if語句:隻保證了在調用wait方法前(階段1)去檢查條件是否滿足,并沒有線上程被喚醒後去檢查是否滿足條件。如果發生了階段3的情況,則execute方法将在不滿足條件的情況下執行了。
notify():
public final void notify()
說明:
1>随機選擇一個在該對象上調用wait方法的線程,賦予其對象鎖,解除其阻塞狀态。
2>在執行notify()方法後,目前線程不會馬上釋放該對象鎖,要等到執行notify()方法的線程退出synchronized方法或synchronized代碼塊後,目前線程才會釋放鎖,此時wait狀态的線程才可以擷取該對象鎖,并繼續執行synchronized代碼塊中wait()方法後面的代碼。
notifyAll():
public final void notifyAll()
說明:喚醒在該對象上調用wait方法等待的所有線程。
說明:wait()、notify()、notifyAll()這三個方法:
1)在調用方法前,線程必須獲得該對象的對象鎖,如果目前線程不是鎖的持有者,方法将抛出一個IllegalMonitorStateException異常(是RuntimeException的子類,故不需要捕獲)。
2)隻能在非靜态同步方法或非靜态同步代碼塊内調用,而且必須由鎖對象來調用方法:
1>這3個方法都是非靜态方法,且這3個方法必須由鎖對象調用,是以我們不能在靜态同步方法和靜态代碼塊中調用(靜态同步方法的鎖是類的Class鎖)。
2>synchronized修飾的非靜态方法:因為this就是鎖對象,是以可以在同步方法中調用這三個方法(可以将this省略)。
3>synchronized修飾的非靜态同步代碼塊:必須使用鎖對象來調用這三個方法。
3)由于wait,notify和notifyAll都是鎖級别的操作,而鎖屬于對象,故把他們定義在Object類中,而不是定義在Thread類中。