Java
、Thread
、線程阻塞
、method
解析Java Thread 類方法
-
sleep()
-
suspend()
-
resume()
-
yield()
-
wait()
-
notify()
線程的阻塞
線程的阻塞發(fā)生原因是什么?
- 為了解決對(duì)
共享存儲(chǔ)區(qū)
的訪問(wèn)沖突,Java 引入了同步機(jī)制
,現(xiàn)在讓我們來(lái)考察多個(gè)線程對(duì)共享資源的訪問(wèn),顯然同步機(jī)制已經(jīng)不夠了,因?yàn)樵谌我鈺r(shí)刻所要求的資源不一定已經(jīng)準(zhǔn)備好了被訪問(wèn),反過(guò)來(lái),同一時(shí)刻準(zhǔn)備好了的資源也可能不止一個(gè)。為了解決這種情況下的訪問(wèn)控制問(wèn)題,Java 引入了對(duì)阻塞機(jī)制的支持。
什么是線程阻塞?
- 阻塞指的是暫停一個(gè)線程的執(zhí)行以等待某個(gè)條件發(fā)生(如某資源就緒),學(xué)過(guò)操作系統(tǒng)的同學(xué)對(duì)它一定已經(jīng)很熟悉了。Java 提供了大量方法來(lái)支持阻塞,下面讓我們逐一分析。
Thread方法解析
sleep() 方法:
sleep()
允許 指定以毫秒為單位的一段時(shí)間作為參數(shù),它使得線程在指定的時(shí)間內(nèi)進(jìn)入阻塞狀態(tài),不能得到CPU 時(shí)間,指定的時(shí)間一過(guò),線程重新進(jìn)入可執(zhí)行狀態(tài)。 典型的sleep()
被用在等待某個(gè)資源就緒的情形:測(cè)試發(fā)現(xiàn)條件不滿足后,讓線程阻塞一段時(shí)間后重新測(cè)試,直到條件滿足為止。suspend() 和 resume() 方法:兩個(gè)方法配套使用,
suspend()
使得線程進(jìn)入阻塞狀態(tài),并且不會(huì)自動(dòng)恢復(fù),必須其對(duì)應(yīng)的resume()
被調(diào)用,才能使得線程重新進(jìn)入可執(zhí)行狀態(tài)。典型的suspend()
和resume()
被用在等待另一個(gè)線程產(chǎn)生的結(jié)果的情形:測(cè)試發(fā)現(xiàn)結(jié)果還沒(méi)有產(chǎn)生后,讓線程阻塞,另一個(gè)線程產(chǎn)生了結(jié)果后,調(diào)用resume()
使其恢復(fù)。yield() 方法:
yield()
使得線程放棄當(dāng)前分得的 CPU 時(shí)間,但是不使線程阻塞,即線程仍處于可執(zhí)行狀態(tài),隨時(shí)可能再次分得 CPU 時(shí)間。調(diào)用yield()
的效果等價(jià)于調(diào)度程序認(rèn)為該線程已執(zhí)行了足夠的時(shí)間從而轉(zhuǎn)到另一個(gè)線程.wait() 和 notify() 方法:兩個(gè)方法配套使用,
wait()
使得線程進(jìn)入阻塞狀態(tài),它有兩種形式,一種允許 指定以毫秒為單位的一段時(shí)間作為參數(shù),另一種沒(méi)有參數(shù),前者當(dāng)對(duì)應(yīng)的notify()
被調(diào)用或者超出指定時(shí)間時(shí)線程重新進(jìn)入可執(zhí)行狀態(tài),后者則必須對(duì)應(yīng)的notify()
被調(diào)用。
方法概述
首先,前面敘述的所有方法都隸屬于
Thread
類,但是這一對(duì)卻直接隸屬于Object
類,也就是說(shuō),所有對(duì)象都擁有這一對(duì)方法。初看起來(lái)這十分不可思議,但是實(shí)際上卻是很自然的,因?yàn)檫@一對(duì)方法阻塞時(shí)要釋放占用的鎖,而鎖是任何對(duì)象都具有的,調(diào)用任意對(duì)象的wait()
方法導(dǎo)致線程阻塞,并且該對(duì)象上的鎖被釋放。而調(diào)用 任意對(duì)象的notify()
方法則導(dǎo)致因調(diào)用該對(duì)象的wait()
方法而阻塞的線程中隨機(jī)選擇的一個(gè)解除阻塞(但要等到獲得鎖后才真正可執(zhí)行)。其次,前面敘述的所有方法都可在任何位置調(diào)用,但是這一對(duì)方法卻必須在
synchronized
方法或塊中調(diào)用,理由也很簡(jiǎn)單,只有在synchronized
方法或塊中當(dāng)前線程才占有鎖,才有鎖可以釋放。同樣的道理,調(diào)用這一對(duì)方法的對(duì)象上的鎖必須為當(dāng)前線程所擁有,這樣才有鎖可以釋放。因此,這一對(duì)方法調(diào)用必須放置在這樣的synchronized
方法或塊中,該方法或塊的上鎖對(duì)象就是調(diào)用這一對(duì)方法的對(duì)象。若不滿足這一條件,則程序雖然仍能編譯,但在運(yùn)行時(shí)會(huì)出現(xiàn)IllegalMonitorStateException
異常。
關(guān)于 wait() 和 notify() 方法最后再說(shuō)明兩點(diǎn):
調(diào)用
notify()
方法導(dǎo)致解除阻塞的線程是從因調(diào)用該對(duì)象的wait()
方法而阻塞的線程中隨機(jī)選取的,我們無(wú)法預(yù)料哪一個(gè)線程將會(huì)被選擇,所以編程時(shí)要特別小心,避免因這種不確定性而產(chǎn)生問(wèn)題。除了
notify()
,還有一個(gè)方法notifyAll()
也可起到類似作用,唯一的區(qū)別在于,調(diào)用 notifyAll() 方法將把因調(diào)用該對(duì)象的 wait() 方法而阻塞的所有線程一次性全部解除阻塞。當(dāng)然,只有獲得鎖的那一個(gè)線程才能進(jìn)入可執(zhí)行狀態(tài)。