1.線程的生命周期
線程的生命周期有新建(New)、就緒(Runnable)、運(yùn)行(Running)、阻塞(Blocked)、和死亡(Dead)5種狀態(tài)
線程狀態(tài)轉(zhuǎn)換
1.1新建和就緒狀態(tài)
- 當(dāng)程序使用new關(guān)鍵字創(chuàng)建線程后,該線程處理新建狀態(tài),java虛擬機(jī)為其分配內(nèi)存并初始化變量。
- 當(dāng)線程對(duì)象調(diào)用了start()方法后,該線程處于就緒狀態(tài),但是線程并未運(yùn)行,等待Java虛擬機(jī)線程調(diào)度器的調(diào)度。==啟動(dòng)線程使用的是start()方法,而不是run()方法!== 如調(diào)用的某線程對(duì)象的run()方法,則該線程不再處于新建(new)狀態(tài)。 start()方法只適用于處于新建狀態(tài)的線程,不然會(huì)引發(fā)IllegalThreadStateException異常。
1.2運(yùn)行和阻塞狀態(tài)
- 當(dāng)處于就緒(Runnable)狀態(tài)的線程獲取了CPU資源,開始執(zhí)行run()方法的線程執(zhí)行體,則該線程處于運(yùn)行狀態(tài),但該線程一般不會(huì)一直處于運(yùn)行狀態(tài),系統(tǒng)會(huì)在一定時(shí)間后剝奪線程占用的資源,讓其他線程獲得執(zhí)行的機(jī)會(huì)。
- 當(dāng)發(fā)生如下情況時(shí)線程會(huì)進(jìn)入阻塞狀態(tài)。
- 線程調(diào)用sleep()方法主動(dòng)放棄所有占用的處理器資源。
- 線程調(diào)用了一個(gè)阻塞式IO方法,方法返回之前,該線程被阻塞。
- 線程試圖獲得一個(gè)同步監(jiān)視器,但該同步監(jiān)視器正在被其他線程所持有,后面會(huì)詳細(xì)介紹同步監(jiān)視器。
- 線程在等待某個(gè)通知(notify)。
- 程序調(diào)用了線程的suspend()方法將線程掛起。但這個(gè)方法容易導(dǎo)致死鎖,所以應(yīng)該盡量避免使用該方法。
- 當(dāng)正在執(zhí)行的線程被阻塞時(shí),其他線程會(huì)獲得被執(zhí)行的機(jī)會(huì)。==被阻塞的線程會(huì)在合適的時(shí)候重新進(jìn)入就緒(Runable)狀態(tài),而不是運(yùn)行(Running)狀態(tài)。==
-發(fā)生以下特定的情況時(shí)可以解除線程的阻塞狀態(tài),讓線程進(jìn)入就緒狀態(tài)。- 調(diào)用sleep()方法的線程經(jīng)過(guò)了指定的時(shí)間。
- 線程調(diào)用的阻塞式IO方法已經(jīng)返回。
- 線程成功地獲得了試圖取得的同步監(jiān)視器。
- 線程正在等待某個(gè)通知時(shí),其他線程發(fā)出了一個(gè)通知。
- 處于掛起狀態(tài)的線程被調(diào)用了resume()恢復(fù)方法。
- 調(diào)用yield()方法可以讓運(yùn)行的線程轉(zhuǎn)入就緒狀態(tài)。
1.3線程死亡
- 線程結(jié)束后會(huì)處于死亡狀態(tài)。以下是線程結(jié)束的三種方法
- run()或call()方法執(zhí)行完成,線程正常結(jié)束。
- 線程拋出一個(gè)未捕獲的Exception 或 Rrror。
- 直接調(diào)用該線程的stop()方法結(jié)束線程,容易導(dǎo)致死鎖,不推薦使用。
- 檢測(cè)某線程是否死亡,可以使用線程對(duì)象的 isAlive()方法,當(dāng)線程處于就緒(Runnable)、運(yùn)行(Running)、阻塞(Blocked)時(shí),返回true;當(dāng)線程處于新建(New)和死亡(Dead)狀態(tài)時(shí)返回false。
2.控制線程
2.1 join線程
- join()方法是由Thread類提供的讓一個(gè)線程等待另一個(gè)線程完全完成的方法。當(dāng)在某個(gè)程序執(zhí)行流中調(diào)用其他線程的join()方法時(shí),調(diào)用線程會(huì)被阻塞,直到j(luò)oin方法加入的join線程執(zhí)行完為止。join()方法有以下三種重載形式:
- join():等待被join的線程執(zhí)行完成。
- join(long millis):等待被join線程時(shí)間最長(zhǎng)為millis毫秒。如果在mills毫秒內(nèi)被join的線程還沒執(zhí)行結(jié)束,則不再等待
2.2 后臺(tái)線程
- 在后臺(tái)運(yùn)行,為其他線程提供服務(wù),這種線程被稱為"后臺(tái)線程“(Daemon Thread)”,又成為“守護(hù)進(jìn)程”,“精靈線程”。后臺(tái)線程特點(diǎn)是如果所有的前臺(tái)線程都死亡,后臺(tái)線程會(huì)自動(dòng)死亡。
- setDaemon(true):該方法將指定的線程設(shè)置成后臺(tái)線程。必須在start()方法前執(zhí)行,否則會(huì)返回IllegalThreadStateException異常。
- isDaemon():該方法用于判斷指定線程是否為后臺(tái)線程。
2.3 線程睡眠:sleep
- Thread類的靜態(tài)方法sleep(),可以讓當(dāng)前執(zhí)行的進(jìn)程暫停一段時(shí)間,并進(jìn)入阻塞狀態(tài)。
- static void sleep(long millis):讓當(dāng)前執(zhí)行的線程暫停mills毫秒,并進(jìn)入阻塞狀態(tài)。
2.4 線程讓步:yield
- yield()方法和sleep()方法有點(diǎn)相似,同樣是由Thread類提供的靜態(tài)方法,它可以讓當(dāng)前執(zhí)行的線程暫停,但他不會(huì)阻塞該線程,只是將該線程轉(zhuǎn)入就緒狀態(tài),從而讓系統(tǒng)的線程調(diào)度器重新調(diào)度一次。當(dāng)某個(gè)線程調(diào)用了yield()方法暫停后,只有與當(dāng)前線程相同或者更高優(yōu)先級(jí)的線程才會(huì)重新獲得執(zhí)行的機(jī)會(huì)。
- yield()方法和sleep()方法的差異:
- sleep()方法暫停當(dāng)前線程后,會(huì)給其他線程執(zhí)行的機(jī)會(huì),不會(huì)理會(huì)其他線程的優(yōu)先級(jí);但yield()方法只會(huì)給優(yōu)先級(jí)相同,或優(yōu)先級(jí)更高的線程執(zhí)行機(jī)會(huì)。
- sleep()方法會(huì)將線程轉(zhuǎn)為阻塞狀態(tài),直到經(jīng)過(guò)阻塞時(shí)間會(huì)轉(zhuǎn)入就緒狀態(tài);而yield()不會(huì)將線程轉(zhuǎn)入阻塞狀態(tài),它只是強(qiáng)制將當(dāng)前線程進(jìn)入就緒狀態(tài)。
- sleep()方法聲明拋出了InterruptedException異常,所以調(diào)用sleep()方法要么捕捉該異常,要么顯示拋出該異常;yield()方法沒有拋出任何異常。
- sleep()方法比yield()方法有更好的可移植性,通常不建議使用yield()方法來(lái)控制并發(fā)線程的執(zhí)行。
2.5 改變線程優(yōu)先級(jí)
- 每個(gè)線程執(zhí)行的時(shí)候都具有一定的優(yōu)先級(jí),優(yōu)先級(jí)較高的線程獲得較多的執(zhí)行機(jī)會(huì)
- 每個(gè)線程默認(rèn)的優(yōu)先級(jí)都與它的父線程的優(yōu)先級(jí)相同。
- Thread類提供了 setPriority(int newPriority)、getPriority()方法來(lái)設(shè)置和返回指定線程的優(yōu)先級(jí),其中setPriority()方法的參數(shù)可以是一個(gè)整數(shù),范圍是1~10之間,但是優(yōu)先級(jí)的級(jí)別需要操作系統(tǒng)的支持,比如windows 2000僅提供了7個(gè)優(yōu)先級(jí),所以應(yīng)該使用如下三個(gè)靜態(tài)常量代表優(yōu)先級(jí),來(lái)確保程序可移植性。
- MAX_PRIORITY:其值為 10。
- MIN_PRIORITY:其值為 1。
- NORM_PRIORITY:其值為 5。