本文內(nèi)容
Java進(jìn)行線程調(diào)度的方法.
如何讓線程進(jìn)入等待.
wait set概念的理解.
如何激活等待狀態(tài)的線程.
進(jìn)行線程協(xié)調(diào)的方法
從上一章《線程的互斥》中我們知道,當(dāng)方法體或代碼塊被synchronized方法修飾時(shí),有一個(gè)線程在執(zhí)行這部分代碼時(shí),其它線程無法同時(shí)執(zhí)行這部分代碼。但如果我們想進(jìn)行更高效的處理如主動(dòng)調(diào)整線程而不是讓線程被動(dòng)等待和盲目競(jìng)爭(zhēng)時(shí)該怎么處理呢?
在Java中,有三個(gè)方法可以對(duì)線程進(jìn)行主動(dòng)高效的控制,它們是wait,notify和notifyAll。
wait是主動(dòng)讓線程等待的方法,而notify和notifyAll則是激活等待中的線程的方法。他們都是Object類的方法,也就是說任何對(duì)象都可以使用這三個(gè)方法。
Wait Set-線程的休息室
在學(xué)習(xí)使用wait,notify和notifyAll這三個(gè)方法之前,我們可以先理解一下Wait Set的概念,它是一個(gè)在某實(shí)例執(zhí)行wait方法時(shí),停止操作的線程的集合,類似于線程的休息室,每個(gè)實(shí)例都擁有這樣一個(gè)休息室。
Wait方法是用來把線程請(qǐng)入這個(gè)休息室的,而notify和notifyAll這兩個(gè)方法是用來將進(jìn)入休息室休息的線程激活的。
wait Set是一個(gè)虛擬的概念,它既不是實(shí)例的字段,也不是可以獲取在實(shí)例上wait中線程的列表的方法.它只是用來幫助我們理解線程的等待和激活的。
Wait方法,將線程放入Wait Set
使用Wait方法時(shí),線程即進(jìn)入Wait set。如線程在執(zhí)行代碼時(shí)遇到這樣的語句:xxObj.wait();則目前的線程會(huì)暫時(shí)停止運(yùn)行,進(jìn)入實(shí)例xxObj的wait Set.
當(dāng)線程進(jìn)入Wait Set時(shí),即釋放了對(duì)該實(shí)例的鎖定.也就是說,即使是被synchronized修飾的方法和代碼塊,當(dāng)?shù)谝粋€(gè)線程進(jìn)入實(shí)例的wait Set等待后,其它線程就可以再進(jìn)入這部分代碼了.
wait()前如果不寫某對(duì)象表示其前面的對(duì)象是this, wait()=this.wait();
notify方法-從wait set中激活一個(gè)線程
使用notify方法時(shí),程序會(huì)從處于等待的實(shí)例的休息室中激活一個(gè)線程.代碼如下:
xxObj.notify();程序?qū)膞xObj的wait set中挑出一個(gè)激活.這個(gè)線程即準(zhǔn)備退出wait set.當(dāng)當(dāng)前線程釋放對(duì)xxObj的鎖定后,這個(gè)線程即獲取這個(gè)鎖定,從上次的停止點(diǎn)-執(zhí)行wait的地方開始運(yùn)行。
線程必須有調(diào)用的實(shí)例的鎖定,才能執(zhí)行notify方法.
Wait set中處于等待狀態(tài)的線程有多個(gè),具體激活那一個(gè)依環(huán)境和系統(tǒng)而變,事先無法辯明.我們大致可理解為隨機(jī)挑選了一個(gè).
notifyAll方法-激活wait set中所有等待的線程
當(dāng)執(zhí)行notifyAll方法時(shí),實(shí)例中所有處于等待狀態(tài)的線程都會(huì)被激活.代碼為:
xxObj.notifyAll();執(zhí)行此代碼后xxObj的wait set中所有處于等待中的線程都會(huì)被激活.但具體那個(gè)線程獲取執(zhí)行wait方法時(shí)釋放的鎖定要靠競(jìng)爭(zhēng),最終只能有一個(gè)線程獲得鎖定,其它的線程只能繼續(xù)回去等待.
notifyAll與notify的差異在于notifyAll激活所有wait set中等待的線程,而notify只激活其中的一個(gè).
該使用notify還是notifyAll
建議:
1) 選擇notifyAll比notify穩(wěn)當(dāng)安全,如果notify處理得不好,程序會(huì)有隱患.
2) 選擇notifyAll比notify可靠,是大多數(shù)程序的首選.
3) 當(dāng)你對(duì)代碼已經(jīng)很清楚,對(duì)線程理解也很透徹時(shí),你可以選擇使用notify,發(fā)揮其處理速度高的優(yōu)勢(shì).
當(dāng)前線程必須持有欲調(diào)用實(shí)例的鎖定,才能調(diào)用wait,notify和notifyAll這三個(gè)方法.
如果代碼是xxObj.notifyAll(或wait, notify)(),則這行代碼必須處于synchronized(xxObj){…}代碼塊中.
如果代碼是this.notifyAll(或wait, notify)(),則這行代碼必須處于synchronized修飾的方法中.
前面說過, notifyAll和notify會(huì)激活線程去獲得進(jìn)入wait時(shí)釋放的鎖定,但這個(gè)鎖定要等剛才執(zhí)行notifyAll或notify方法的線程釋放這個(gè)鎖定才能獲取.
總結(jié)
1) wait,notify和notifyAll都是java.lang.Object的方法,它們用來對(duì)線程進(jìn)行調(diào)度.
2) obj.wait()是把執(zhí)行這句的線程放入obj的wait set中.
3) obj.notify()是從wait set中喚醒一個(gè)線程,這個(gè)線程在當(dāng)前線程釋放對(duì)obj的鎖定后即獲取這個(gè)鎖定.
4) obj.notifyAll()是喚醒所有在obj的wait set中的線程,這批線程在當(dāng)前線程釋放obj的鎖定后去競(jìng)爭(zhēng)這個(gè)鎖定,最終只有一個(gè)能獲得,其它的又重新返回wait set等待下一次激活.
5) 執(zhí)行這個(gè)wait,notify和notifyAll這三個(gè)方法前,當(dāng)前線程(即執(zhí)行obj.wait(), obj.notify()和obj.notifyAll()代碼的線程)必須持有obj的鎖定.