等待和通知
API
java.lang.Object
類提供了一套等待/通知的 API疾渣,它由 3 個(gè) wait()苟鸯、一個(gè) notify() 和一個(gè) notifyAll() 方法組成。wait() 方法等待某個(gè)條件成立,當(dāng)這個(gè)條件成立時(shí),notify() 和 notifyAll() 方法通知處于等待中的線程。
-
void wait()
:導(dǎo)致當(dāng)前線程一直處于等待蛤克,直到另外的線程調(diào)用這個(gè)對(duì)象的notify()
或者notifyAll()
方法,又或者一直等待其他的線程中斷當(dāng)前等待的線程夷蚊。 -
void wait(long timeout)
:其他定義和void wait()
一致构挤,多了等待特定的毫秒數(shù)(由 timeout 確定)。當(dāng) timeout 是負(fù)數(shù)的時(shí)候惕鼓,這個(gè)方法拋出java.lang.IllegalArgumentException
筋现。 -
void wait(long timeout, int nanos)
:其他定義和void wait()
一致,多了等待特定的毫秒(由 timeout 確定)和納秒數(shù)(由 nanos 確定)箱歧。當(dāng) timeout 是負(fù)數(shù)矾飞、nanos 是負(fù)數(shù)、或者 nanos 大于 999999 的時(shí)候呀邢,這個(gè)方法拋出java.lang.IllegalArgumentException
洒沦。 -
void notify()
:?jiǎn)拘颜诘却搶?duì)象監(jiān)聽器的單條線程。如果有幾條線程在該對(duì)象上等待价淌,其中某一條會(huì)被挑選出來(lái)喚醒申眼,這種選擇是隨意的且取決于具體實(shí)現(xiàn)。 -
void notifyAll()
:?jiǎn)拘颜诘却搶?duì)象監(jiān)聽器的全部線程蝉衣。
注意:
- 若當(dāng)前線程開始或正在等待通知括尸,任意線程中斷了它,3 個(gè)
wait()
方法都會(huì)拋出java.lang.InterruptedException
病毡。 - 絕對(duì)不要在循環(huán)外面調(diào)用
wait()
濒翻、notify()
和notifyAll()
方法。(防止出現(xiàn) lost-wake-up 問(wèn)題) - 在應(yīng)用程序中啦膜,僅有兩條線程并且某條線程偶爾等待有送、需要被另外一條線程通知的時(shí)候,才使用
notify()
方法功戚,否則使用notifyAll()
方法娶眷。
為什么 wait 方法定義在 Object 類里面,而不是 Thread 類啸臀?
- 同步和等待是兩個(gè)不同的領(lǐng)域届宠,同步是提供互斥并確保 Java 類的線程安全的烁落,wait 和 notify 是兩個(gè)線程之間的通信機(jī)制
- 保證每個(gè)對(duì)象都可上鎖
為什么 wait 方法要放到同步塊中?
為了避免使用者出現(xiàn) "Lost-Wake-Up" 問(wèn)題豌注。
關(guān)于什么是 "Lost-Wait-Up" 問(wèn)題伤塌,可以參考狼叔的博客:
http://www.reibang.com/p/b8073a6ce1c0
總結(jié)如下
- Java 強(qiáng)制我們的
wait()
/notify()
調(diào)用必須要在一個(gè)同步塊中,轧铁,就是不想讓我們?cè)诓唤?jīng)意間出現(xiàn)這種 lost wake up問(wèn)題每聪。 - 不僅僅是這兩個(gè)方法,包括
java.util.concurrent.locks.Condition
的await()
/signal()
也必須要在同步塊中 - Java 要求上述四個(gè)方法必須放在鎖對(duì)象的代碼塊中齿风,即使加鎖药薯,不是鎖對(duì)象的代碼塊中同樣也會(huì)報(bào)錯(cuò),Java 這種檢測(cè)機(jī)制非常嚴(yán)格救斑。
如下述代碼童本,加鎖的是 obj 對(duì)象,notify 卻是 anotherObj 對(duì)象使用脸候,會(huì)拋出 IllegalMonitorStateException 異常穷娱。
private Object obj = new Object();
private Object anotherObj = new Object();
@Test
public void produce() {
synchronized (obj) {
try {
anotherObj.notify();
} catch (Exception e) {
e.printStackTrace();
}
}
}