對(duì)于線程的定義,這里就不贅述了。但是有一點(diǎn)需要明確,對(duì)于單核處理器,即任何一個(gè)時(shí)候如果只有一顆CPU在工作,那么就只可能存在線程并發(fā),不存在線程并行。
而線程之所有能夠并發(fā),也主要是源于現(xiàn)代CPU處理數(shù)據(jù)太快了,而將數(shù)據(jù)從內(nèi)存讀入到CPU的寄存器的速度相對(duì)而言太慢了。為了充分利用CPU的處理能力,不讓CPU處于苦苦等待數(shù)據(jù),采取允許多個(gè)線程在一段時(shí)間內(nèi)“共同”運(yùn)行。這里的共同兩字使用引號(hào),其實(shí)是說多個(gè)線程從較長(zhǎng)的時(shí)間段來(lái)看,他們好似同時(shí)在運(yùn)行。但其實(shí)具體到每一剎那,其實(shí)CPU仍只是處理一個(gè)線程的工作內(nèi)容。
下面進(jìn)入正題,首先介紹線程的狀態(tài)。既然一個(gè)線程不是始終占用CPU,一個(gè)CPU通常也不會(huì)始終只處理一個(gè)線程的工作內(nèi)容,那么必然的,線程在不同階段,對(duì)外部而言就要予以標(biāo)識(shí),使得外部能夠獲知的線程的確切狀態(tài),以便進(jìn)行相應(yīng)的處理。
自JDK1.0起,Thread類就被引入了,其持有一個(gè)靜態(tài)內(nèi)部枚舉State,共有六種取值。

NEW狀態(tài):是實(shí)例化Thread類時(shí)的初始默認(rèn)狀態(tài)
RUNABLE狀態(tài):是Thread實(shí)例運(yùn)行的狀態(tài)。這里實(shí)際上可再分為兩種,READY和RUNING狀態(tài)。從名字可以看出,RUNABLE只是表示具備運(yùn)行的條件,但是不一定正在被執(zhí)行。之所有對(duì)外不再將RUNABLE細(xì)分READY和RUNING狀態(tài),是因?yàn)檫@交給了JVM虛擬機(jī)來(lái)決定。既然如此,也就沒必要對(duì)外暴露兩種狀態(tài)給JDK的使用者來(lái)感知。
BLOCKED狀態(tài):是Thread實(shí)例等待獲取某個(gè)鎖的狀態(tài)。
WAITING狀態(tài):是Thread實(shí)例無(wú)限期的等待另一個(gè)線程做出某個(gè)特定行為的狀態(tài)。
TIMED_WAITING: 和WAITING狀態(tài)類似,但是處于該狀態(tài)的Thread實(shí)例只會(huì)等待一段時(shí)間,如在這段時(shí)間內(nèi),該Thread實(shí)例沒有“等到”另一線程做出某個(gè)特定行為,那么時(shí)間期滿,視為已“等到”。
TERMINATED狀態(tài):是Thread實(shí)例已終止,不再有可能被執(zhí)行了的狀態(tài)。
這些狀態(tài)之間的轉(zhuǎn)換由下圖表明:

下面對(duì)其中設(shè)計(jì)的重要方法進(jìn)行一一說明
wait方法:該實(shí)例方法的正確使用方法如下圖所示

首先,調(diào)用任何對(duì)象上的wait方法之前必須取得該對(duì)象上的鎖,這也就是需要用sychronized關(guān)鍵字的原因。sychronized關(guān)鍵字保證了,線程只有在獲取了obj的鎖后,才會(huì)進(jìn)入緊跟其后的代碼塊,進(jìn)而調(diào)用obj.wait方法。倘若未能獲取obj對(duì)象的鎖,當(dāng)線程執(zhí)行到obj的wait方法時(shí),就會(huì)拋出IllegalMonitorStateException,這是一個(gè)unchecked Exception。而當(dāng)線程獲取obj對(duì)象的鎖后,當(dāng)線程執(zhí)行到obj的wait方法時(shí),線程轉(zhuǎn)為WAITING狀態(tài),與此同時(shí)線程會(huì)釋放obj的鎖。
其次,也許你已經(jīng)注意到了,obj.wait方法通常處在一個(gè)while循環(huán)體中。通常情況下,必然是處于某種情況A,線程不可再運(yùn)行下去,迫于無(wú)奈通過調(diào)用obj.wait方法的這種方式,使得這個(gè)持有obj對(duì)象的鎖的線程,處于WAITING狀態(tài)。此后某一時(shí)刻,當(dāng)發(fā)生了某種特定動(dòng)作(其實(shí)就是其他線程調(diào)用了同一個(gè)obj上的notify或notifyAll方法,這里先不詳說),并且obj.wait方法所處的線程重新獲得obj的鎖之后,此時(shí)某種特殊情況A仍有成立的可能,因此這里需要一個(gè)while循環(huán),進(jìn)行再一次的判斷。這里常犯的錯(cuò)誤是,采取if語(yǔ)句。
最后,wait的方法簽名上有throws?InterruptedException。這一方面要求了,程序在編碼時(shí)如果調(diào)用了obj的wait時(shí),需要顯示的進(jìn)行異常處理(自己捕獲,或者向上拋出)。另一方面也說明了,當(dāng)線程因調(diào)用obj的wait方法而處于WAITING狀態(tài)時(shí),是能夠響應(yīng)中斷的。
notify和notifyAll方法:有了obj的wait方法做鋪墊,notify和notifyAll就很好理解了。下面以notify方法開始討論。

首先,和wait方法類似,線程只有在獲取了obj的鎖后,才會(huì)進(jìn)入緊跟其后的代碼塊,進(jìn)而調(diào)用obj.notify方法。
其次,notify方法只保證因wait轉(zhuǎn)入WAITING狀態(tài)的狀態(tài)被喚醒,不保證被喚醒的線程一定能夠獲得obj對(duì)象的鎖。
最后調(diào)用notify方法,不會(huì)釋放所在線程所持有的obj的鎖。當(dāng)線程執(zhí)行到notify方法,對(duì)該線程而言,線程狀態(tài)不會(huì)發(fā)生改變,因此這里和wait方法不同,不存在什么響應(yīng)中斷的機(jī)制。
而notify方法和notifyAll方法的不同之處在于:倘若有其他的多個(gè)線程因?yàn)閣ait方法轉(zhuǎn)入WAITING狀態(tài),notify方法只會(huì)喚醒多個(gè)線程中的一個(gè),至于是哪一個(gè),這不為JAVA開發(fā)者所控制。而notifyAll方法會(huì)喚醒多個(gè)線程中的所有線程,當(dāng)然也僅僅只是喚醒,由于一個(gè)對(duì)象的鎖只有一個(gè),因?yàn)檫@些被喚醒的線程還需要去競(jìng)爭(zhēng)鎖,JVM會(huì)決定由哪一個(gè)線程獲得鎖,其他未獲得鎖的線程因此進(jìn)入BLOCKED狀態(tài)(因?yàn)榇藭r(shí)在等待進(jìn)入sychronized代碼塊)
Thread對(duì)象的join實(shí)例方法:

首先,請(qǐng)未必注意,join方法是Thread實(shí)例的方法,普通的Object對(duì)象不具有這個(gè)方法。
此外,更重要的一點(diǎn)是,是調(diào)用join的線程轉(zhuǎn)入WAITING狀態(tài),即主線程轉(zhuǎn)入WAITING狀態(tài),而不是t線程。當(dāng)t線程執(zhí)行結(jié)束,主線程從WAITING才裝入RUNNABLE狀態(tài)。因此輸出hello字符串1秒鐘之后,world才會(huì)被輸出。
上面的代碼涉及到了自定義一個(gè)線程,以及如何開啟它的知識(shí),暫時(shí)還未說明。重點(diǎn)關(guān)注主線程和線程t的狀態(tài)變化。
最后,和調(diào)用obj的wait方法一樣,當(dāng)主線程因?yàn)檎{(diào)用了Thread實(shí)例的join方法而轉(zhuǎn)入WAITING狀態(tài)時(shí),依然能夠響應(yīng)中斷。
Thread類的sleep靜態(tài)方法:它的使用在上圖已經(jīng)有所體現(xiàn)。當(dāng)一個(gè)線程調(diào)用它時(shí),會(huì)使得該線程轉(zhuǎn)入TIMED_WAITING一段時(shí)間。需要注意的是,它是一個(gè)靜態(tài)方法。
寫在最后:文中多次出現(xiàn)“中斷”二字,關(guān)于該知識(shí)請(qǐng)關(guān)注博主的多線程之線程中斷