淺談 Java線程狀態(tài)轉(zhuǎn)換及控制

 一個(gè)線程被創(chuàng)建后就進(jìn)入了線程的生命周期镀脂。在線程的生命周期中肯尺,共包括新建(New)、就緒(Runnable)翰守、運(yùn)行(Running)、阻塞(Blocked)和死亡(Dead)這五種狀態(tài)疲酌。當(dāng)線程啟動(dòng)以后蜡峰,CPU需要在多個(gè)線程之間切換,所以線程也會(huì)隨之在運(yùn)行朗恳、阻塞湿颅、就緒這幾種狀態(tài)之間切換。

  線程的狀態(tài)轉(zhuǎn)換如圖:


  當(dāng)使用new關(guān)鍵字創(chuàng)建一個(gè)線程對(duì)象后粥诫,該線程就處于新建狀態(tài)油航。此時(shí)的線程就是一個(gè)在堆中分配了內(nèi)存的靜態(tài)的對(duì)象,線程的執(zhí)行體(run方法的代碼)不會(huì)被執(zhí)行怀浆。

  當(dāng)調(diào)用了線程對(duì)象的start()方法后谊囚,該線程就處于就緒狀態(tài)怕享。此時(shí)該線程并沒有開始運(yùn)行,而是處于可運(yùn)行池中秒啦,Java虛擬機(jī)會(huì)為該線程創(chuàng)建方法調(diào)用棧和程序計(jì)數(shù)器。至于該線程何時(shí)才能運(yùn)行搀玖,要取決于JVM的調(diào)度余境。

  一旦處于就緒狀態(tài)的線程獲得CPU 開始運(yùn)行,該線程就進(jìn)入了運(yùn)行狀態(tài)灌诅。線程運(yùn)行時(shí)會(huì)執(zhí)行run方法的代碼芳来。對(duì)于搶占式策略的操作系統(tǒng),系統(tǒng)會(huì)為每個(gè)可執(zhí)行的線程分配一個(gè)時(shí)間片猜拾,當(dāng)該時(shí)間片用盡后即舌,系統(tǒng)會(huì)剝奪該線程所占有的處理器資源,從而讓其他線程獲得占有CPU 而運(yùn)行的機(jī)會(huì)挎袜。此時(shí)該線程會(huì)從運(yùn)行態(tài)轉(zhuǎn)為就緒態(tài)顽聂。

Java學(xué)習(xí)交流群:1106441130? 歡迎討論交流,另外可免費(fèi)領(lǐng)取一份(Java學(xué)習(xí)視頻盯仪,技術(shù)文檔紊搪,電子書籍,面試等資料...)

當(dāng)一個(gè)正在運(yùn)行的線程遇到如下情況時(shí)全景,線程會(huì)從運(yùn)行態(tài)轉(zhuǎn)為阻塞態(tài):

   ∫① 線程調(diào)用sleep、join等方法爸黄。

   ≈臀啊② 線程調(diào)用了一個(gè)阻塞式IO方法。

   】还蟆③ 線程試圖獲得一個(gè)同步監(jiān)視器梆奈,但是該監(jiān)視器正在被其他線程持有。

   〕瓶④ 線程在等待某個(gè) notify 通知鉴裹。

    ⑤ 程序調(diào)用了線程的suspend方法將該線程掛起钥弯。

  當(dāng)線程被阻塞后径荔,其他線程就有機(jī)會(huì)獲得CPU資源而被執(zhí)行。當(dāng)上述導(dǎo)致線程被阻塞的因素解除后脆霎,線程會(huì)回到就緒狀態(tài)等待處理機(jī)調(diào)度而被執(zhí)行总处。

  當(dāng)一個(gè)線程執(zhí)行結(jié)束后,該線程進(jìn)入死亡狀態(tài)睛蛛。

有以下3種方式可結(jié)束一個(gè)線程:

 ○新怼① run 方法執(zhí)行完畢胧谈。

  ② 線程拋出一個(gè)異齿┢担或錯(cuò)誤菱肖,而該異常或錯(cuò)誤未被捕獲旭从。

 ∥惹俊③ 調(diào)用線程的 stop方法結(jié)束該線程。(不推薦使用)

2|0線程的控制

  Thread類中提供了一些控制線程的方法和悦,通過這些方法可以輕松地控制一個(gè)線程的執(zhí)行和運(yùn)行狀態(tài)退疫,以達(dá)到程序的預(yù)期效果。

2|1join 方法

  如果線程A調(diào)用了線程B的join方法鸽素,線程A將被阻塞褒繁,等待線程B執(zhí)行完畢后線程A才會(huì)被執(zhí)行。這里需要注意一點(diǎn)的是馍忽,join方法必須在線程B的start方法調(diào)用之后調(diào)用才有意義棒坏。join方法的主要作用就是實(shí)現(xiàn)線程間的同步,它可以使線程之間的并行執(zhí)行變?yōu)榇袌?zhí)行遭笋。

join 方法有以下3種重載形式:

 】〉帧① join(): 等待被join的線程執(zhí)行完成。

 ∽荨② join(long millis): 等待被join 的線程的時(shí)間為 millis 毫秒徽诲,如果該線程在millis 毫秒內(nèi)未結(jié)束,則不再等待吵血。

 』烟妗③ join(long millis,int nanos): 等待被join的線程的時(shí)間最長(zhǎng)為 millis 毫秒加上nanos微妙。

publicclassJoinThreadextendsThread{

@Override

? ? public void run() {

for(int i =0; i <20; i++) {

System.out.println(Thread.currentThread().getName() +"---"+ i);

try{

Thread.sleep(500);

}catch(InterruptedExceptione) {

// TODO Auto-generated catch block

? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? }

? ? ? ? }

? ? }

}

publicclassTestThreadState{

publicstaticvoidmain(String[] args){


//? ? ? 創(chuàng)建要加入當(dāng)前線程的線程蹋辅,并啟動(dòng)

JoinThread j1 =newJoinThread();

? ? ? ? j1.start();


//? ? ? 加入當(dāng)前線程钱贯,阻塞當(dāng)前線程,直到加入線程執(zhí)行完畢

try{

j1.join();

}catch(InterruptedException e1) {

// TODO Auto-generated catch block

? ? ? ? ? ? e1.printStackTrace();

? ? ? ? }


for(inti =0; i <20; i++) {

System.out.println(Thread.currentThread().getName());

try{

Thread.sleep(100);

}catch(InterruptedException e) {

// TODO Auto-generated catch block

? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? }

? ? ? ? }

? ? }

}

  我們定義了一個(gè)JoinThread類侦另,它繼承了Thread類秩命,這是我們要加入的線程類。

  在main方法中褒傅,我們創(chuàng)建了JoinThread線程弃锐,并把它加入到當(dāng)前線程(主線程)中,并沒有指定當(dāng)前線程等待的時(shí)間殿托,所以會(huì)一直阻塞當(dāng)前線程霹菊,直到JoinThread線程的run方法執(zhí)行完畢,才會(huì)繼續(xù)執(zhí)行當(dāng)前線程支竹。

2|2sleep 方法

  當(dāng)線程A調(diào)用了 sleep方法旋廷,則線程A將被阻塞鸠按,直到指定睡眠的時(shí)間到達(dá)后,線程A才會(huì)重新被喚起饶碘,進(jìn)入就緒狀態(tài)目尖。

sleep方法有以下2種重載形式:

 ≈橄础① sleep(long millis):讓當(dāng)前正在執(zhí)行的線程暫停millis毫秒屎勘,該線程進(jìn)入阻塞狀態(tài)彻亲。

 『掣邸② sleep(long mills,long nanos):讓當(dāng)前正在執(zhí)行的線程暫停 millis 毫秒加上 nanos微秒。

publicclassTest{

publicstaticvoidmain(String[] args) {

for(inti =0; i <10; i++) {

System.out.println(Thread.currentThread().getName() +"---"+ i);

try{

Thread.sleep(1000);// 阻塞當(dāng)前線程1s

}catch(Exceptione) {

? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? }

? ? ? ? }

? ? }

}

  這段代碼中沒有創(chuàng)建其他線程捶障,只有當(dāng)前線程存在,也就是執(zhí)行main函數(shù)的主線程。for循環(huán)中每打印一次線程名稱鬼吵,主線程就會(huì)被sleep方法阻塞1s,然后進(jìn)入就緒狀態(tài)篮赢,重新等待被調(diào)到齿椅,實(shí)現(xiàn)了線程的控制。

2|3yield 方法

  當(dāng)線程A調(diào)用了yield方法启泣,它可以暫時(shí)放棄處理器涣脚,但是線程A不會(huì)被阻塞,而是進(jìn)入就緒狀態(tài)寥茫。

publicclassYieldThreadextendsThread{

@Override

? ? public void run() {

for(int i =0; i <10; i++) {

System.out.println(Thread.currentThread().getName() +"---"+ i);

// 主動(dòng)放棄

Thread.yield();

? ? ? ? }

? ? }

}

  我們自定義了一個(gè)線程類YieldThread遣蚀,在run方法中定義了一個(gè)for循環(huán),for循環(huán)中每打印一次線程名稱纱耻,就會(huì)調(diào)用一下yield方法芭梯,主動(dòng)放棄CUP讓給其它有相同優(yōu)先級(jí)或更高優(yōu)先級(jí)的線程,自己進(jìn)入就緒狀態(tài)弄喘,等待被CPU調(diào)度玖喘。

2|4設(shè)置線程的優(yōu)先級(jí)

  每個(gè)線程都有自己的優(yōu)先級(jí),默認(rèn)情況下線程的優(yōu)先級(jí)都與創(chuàng)建該線程的父線程的優(yōu)先級(jí)相同蘑志。同時(shí)Thread類提供了setPriority(int priority) 和getPriority()方法設(shè)置和返回指定線程的優(yōu)先級(jí)累奈。參數(shù)priority是一個(gè)整型數(shù)據(jù),用以指定線程的優(yōu)先級(jí)急但。priority 的取值范圍是1-10澎媒,默認(rèn)值為5,也可以使用Thread類提供的三個(gè)靜態(tài)常量設(shè)置線程的優(yōu)先級(jí)。

 〔ㄗ① MAX_PRIORITY:最高優(yōu)先級(jí)旱幼,其值為10。

 ⊥晃② MIN_PRIORITY:最低優(yōu)先級(jí)柏卤,其值為1冬三。

  ③ NORM_PRIORITY:普通優(yōu)先級(jí)缘缚,其值為5勾笆。

publicclassTestThreadPriority{

publicstaticvoidmain(String[] args){

// 線程優(yōu)先級(jí)

ThreadPriority p1 =newThreadPriority();

p1.setName("p1");

ThreadPriority p2 =newThreadPriority();

p2.setName("p2");

ThreadPriority p3 =newThreadPriority();

p3.setName("p3");


p1.setPriority(1);

p3.setPriority(10);


? ? ? ? p1.start();

? ? ? ? p2.start();

? ? ? ? p3.start();

? ? }

}

  我們創(chuàng)建了三個(gè)線程p1、p2桥滨、p3窝爪,設(shè)置了p1的優(yōu)先級(jí)為1,p3的優(yōu)先級(jí)為10齐媒,并沒有設(shè)置p2的蒲每,所以p2的優(yōu)先級(jí)默認(rèn)是5。優(yōu)先級(jí)越高喻括,表示獲取cup的機(jī)會(huì)越多邀杏,注意此處說的是機(jī)會(huì),所以高優(yōu)先級(jí)的線程并不是一定先于低優(yōu)先級(jí)的線程被CPU調(diào)度唬血,只是機(jī)會(huì)更大而已望蜡。

sleep方法和wait方法的區(qū)別是什么?

  sleep 方法是Thread類的一個(gè)靜態(tài)方法拷恨,其作用是使運(yùn)行中的線程暫時(shí)停止指定的毫秒數(shù)脖律,從而該線程進(jìn)入阻塞狀態(tài)并讓出處理器,將執(zhí)行的機(jī)會(huì)讓給其他線程腕侄。但是這個(gè)過程中監(jiān)控狀態(tài)始終保持小泉,當(dāng)sleep的時(shí)間到了之后線程會(huì)自動(dòng)恢復(fù)。

  wait 方法是Object類的方法冕杠,它是用來(lái)實(shí)現(xiàn)線程同步的微姊。當(dāng)調(diào)用某個(gè)對(duì)象的wait方法后,當(dāng)前線程會(huì)被阻塞并釋放同步鎖拌汇,直到其他線程調(diào)用了該對(duì)象的 notify 方法或者 notifyAll 方法來(lái)喚醒該線程柒桑。所以 wait 方法和 notify(或notifyAll)應(yīng)當(dāng)成對(duì)出現(xiàn)以保證線程間的協(xié)調(diào)運(yùn)行。

sleep方法和yield方法的區(qū)別是什么噪舀?

 】尽① sleep方法暫停當(dāng)前線程后,會(huì)給其他線程執(zhí)行機(jī)會(huì)而不會(huì)考慮其他線程的優(yōu)先級(jí)与倡。但是yield方法只會(huì)給優(yōu)先級(jí)相同或者優(yōu)先級(jí)更高的線程執(zhí)行機(jī)會(huì)界逛。

  ② sleep方法執(zhí)行后線程會(huì)進(jìn)入阻塞狀態(tài)纺座,而執(zhí)行了yield方法后息拜,當(dāng)前線程會(huì)進(jìn)入就緒狀態(tài)。

  ③ 由于sleep方法的聲明拋出了 InterruptedException 異常少欺,所以在調(diào)用sleep方法時(shí)需要catch 該異吃辏或拋出該異常,而yield 方法沒有聲明拋出異常赞别。

 ∥飞隆④ sleep 方法比yield 方法具有更好的可移植性。

補(bǔ)充一下sleep仿滔、yield惠毁、join和wait的差異:

  ① sleep崎页、join鞠绰、yield時(shí)并不釋放對(duì)象鎖資源,在wait操作時(shí)會(huì)釋放對(duì)象資源飒焦,wait在被notify/notifyAll喚醒時(shí)蜈膨,重新去搶奪獲取對(duì)象鎖資源。

 』母② sleep丈挟、join刁卜、yield可以在任何地方使用志电,而wait,notify蛔趴,notifyAll只能在同步控制方法或者同步控制塊中使用挑辆。

  ③ 調(diào)用wait會(huì)立即釋放鎖孝情,進(jìn)入等待隊(duì)列鱼蝉,但是notify()不會(huì)立刻釋放sycronized(obj)中的對(duì)象鎖,必須要等notify()所在線程執(zhí)行完sycronized(obj)同步塊中的所有代碼才會(huì)釋放這把鎖箫荡,然后供等待的線程來(lái)?yè)寠Z對(duì)象鎖魁亦。

Java中為什么不建議使用stop和suspend方法終止線程?

  在Java中可以使用stop 方法停止一個(gè)線程羔挡,使該線程進(jìn)入死亡狀態(tài)洁奈。但是使用這種方法結(jié)束一個(gè)線程是不安全的,在編寫程序時(shí)應(yīng)當(dāng)禁止使用這種方法绞灼。

  之所以說stop方法是線程不安全的利术,是因?yàn)橐坏┱{(diào)用了Thread.stop()方法,工作線程將拋出一個(gè)ThreadDeath的異常低矮,這會(huì)導(dǎo)致run方法結(jié)束執(zhí)行印叁,而且結(jié)束的點(diǎn)是不可控的,也就是說,它可能執(zhí)行到run方法的任何一個(gè)位置就突然終止了轮蜕。同時(shí)它還會(huì)釋放掉該線程所持有的鎖昨悼,這樣其他因?yàn)檎?qǐng)求該鎖對(duì)象而被阻塞的線程就會(huì)獲得鎖對(duì)象而繼續(xù)執(zhí)行下去。一般情況下跃洛,加鎖的目的是保護(hù)數(shù)據(jù)的一致性幔戏,然而如果在調(diào)用Thread.stop()后線程立即終止,那么被保護(hù)數(shù)據(jù)就有可能出現(xiàn)不一致的情況(數(shù)據(jù)的狀態(tài)不可預(yù)知)税课。同時(shí)闲延,該線程所持有的鎖突然被釋放,其他線程獲得同步鎖后可以進(jìn)入臨界區(qū)使用這些被破壞的數(shù)據(jù)韩玩,這將有可能導(dǎo)致一些很奇怪的應(yīng)用程序錯(cuò)誤發(fā)生垒玲,而且這種錯(cuò)誤非常難以debug.所以在這里再次重申,不要試圖用stop 方法結(jié)束一個(gè)線程找颓。

  suspend方法可以阻塞一個(gè)線程合愈,然而該線程雖然被阻塞,但它仍然持有之前獲得的鎖击狮,這樣其他任何線程都不能訪問相同鎖對(duì)象保護(hù)的資源佛析,除非被阻塞的線程被重新恢復(fù)。如果此時(shí)只有一個(gè)線程能夠恢復(fù)這個(gè)被suspend的線程彪蓬,但前提是先要訪問被該線程鎖定的臨界資源寸莫。這樣便產(chǎn)生了死鎖。所以在編寫程序時(shí)档冬,應(yīng)盡量避免使用suspend,如確實(shí)需要阻塞一個(gè)線程的運(yùn)行膘茎,最好使用wait方法,這樣既可以阻塞掉當(dāng)前正在執(zhí)行的線程酷誓,同時(shí)又使得該線程不至于陷入死鎖披坏。

  用一句話說就是:stop方法是線程不安全的,可能產(chǎn)生不可預(yù)料的結(jié)果盐数;suspend方法可能導(dǎo)致死鎖棒拂。

如何終止一個(gè)線程?

  在Java 中不推薦使用stop方法和suspend方法終止一個(gè)線程玫氢,因?yàn)槟鞘遣话踩闹闾耄敲匆鯓咏K止一個(gè)線程呢?

方法一:使用退出標(biāo)志

  正常情況下琐旁,當(dāng)Thread 或 Runnable 類的run方法執(zhí)行完畢后該線程即可結(jié)束涮阔,但是有些情況下run方法可能永遠(yuǎn)都不會(huì)停止,例如灰殴,在服務(wù)端程序中使用線程監(jiān)聽客戶端請(qǐng)求敬特,或者執(zhí)行其他需要循環(huán)處理的任務(wù)掰邢。這時(shí)如果希望有機(jī)會(huì)終止該線程,可將執(zhí)行的任務(wù)放在一個(gè)循環(huán)中(例如 while循環(huán))伟阔,并設(shè)置一個(gè)boolean型的循環(huán)結(jié)束的標(biāo)志辣之。如果想使 while 循環(huán)在某一特定條件下退出,就可以通過設(shè)置這個(gè)標(biāo)志為true或false 來(lái)控制 while 循環(huán)是否退出皱炉。這樣將線程結(jié)束的控制邏輯與線程本身邏輯結(jié)合在一起怀估,可以保證線程安全可控地結(jié)束。

  讓我們來(lái)看一看案例:

publicclassTestQuitSign{

// 退出標(biāo)志

publicstaticvolatileboolean quitFlag =false;


// 退出標(biāo)志:針對(duì)運(yùn)行時(shí)的線程

publicstaticvoidmain(String[] args){

// 線程一:每隔一秒合搅,打印一條信息多搀,當(dāng)quitFlag為true時(shí)結(jié)束run方法。

newThread() {

publicvoidrun(){

System.out.println("thread start...");

while(!quitFlag) {

try{

Thread.sleep(1000);

}catch(Exception e) {


? ? ? ? ? ? ? ? ? ? }

System.out.println("thread running...");

? ? ? ? ? ? ? ? }

System.out.println("thread end...");

? ? ? ? ? ? }

? ? ? ? }.start();


// 線程二:等待三秒灾部,設(shè)置quitFlag為true康铭,終止線程一。

newThread() {

publicvoidrun(){

try{

Thread.sleep(3000);

}catch(Exception e) {

//TODO:handle exception

? ? ? ? ? ? ? ? }

quitFlag =true;

? ? ? ? ? ? }

? ? ? ? }.start();


? ? }

}

  在上面這段程序中的main方法里創(chuàng)建了兩個(gè)線程赌髓,第一個(gè)線程的run方法中有一個(gè)while循環(huán)从藤,該循環(huán)通過boolean型變量quitFlag控制其是否結(jié)束。因?yàn)樽兞縬uitFlag的初始值為false,所以如果不修改該變量锁蠕,第一個(gè)線程中的run方法將不會(huì)停止夷野,也就是說,第一個(gè)線程將永遠(yuǎn)不會(huì)終止荣倾,并且每隔1s在屏幕上打印出一條字符串悯搔。第二個(gè)線程的作用是通過修改變量quitFlag來(lái)終止第一個(gè)線程。在第二個(gè)線程的run方法中首先將線程阻塞3s,然后將quitFlag置為true.因?yàn)樽兞縬uitFlag是同一進(jìn)程中兩個(gè)線程共享的變量逃呼,所以可以通過修改quitFlag的值來(lái)控制第一個(gè)線程的執(zhí)行鳖孤。當(dāng)變量quitFlag被置為true,第一個(gè)線程的while循環(huán)就可以終止者娱,所以run方法就能執(zhí)行完畢抡笼,從而安全退出第一個(gè)線程。

  注意黄鳍,boolean 型變量 quitFlag 被聲明為 volatile推姻,volatile 會(huì)保證變量在一個(gè)線程中的每一步操作在另一個(gè)線程中都是可見的,所以這樣可以確保將 quitFlag 置為true 后可以安全退出第一個(gè)線程框沟。

方法二:使用 interrupt方法

  使用退出線程標(biāo)志的方法終止一個(gè)線程存在一定的局限性藏古,主要的限制就是這種方法只對(duì)運(yùn)行中的線程起作用,如果該線程被阻塞(例如忍燥,調(diào)用了 Thread.join()方法或者Thread.sleep()方法等)而處于不可運(yùn)行的狀態(tài)時(shí)拧晕,則退出線程標(biāo)志的方法將不會(huì)起作用。

  在這種情況下梅垄,可以使用Thread 提供的 interrupt()方法終止一個(gè)線程厂捞。因?yàn)樵摲椒m然不會(huì)中斷一個(gè)正在運(yùn)行的線程,但是它可以使一個(gè)被阻塞的線程拋出一個(gè)中斷異常,從而使線程提前結(jié)束阻塞狀態(tài)靡馁,然后通過catch塊捕獲該異常欲鹏,從而安全地結(jié)束該線程。

  我們來(lái)看看下面的例子:

publicclassTestInterrupt{

// Interrupt方法: 針對(duì)阻塞狀態(tài)的線程

publicstaticvoidmain(String[] args) throws InterruptedException{

// 創(chuàng)建線程

Thread thread =newThread() {

publicvoidrun(){

System.out.println("thread start...");

try{

Thread.sleep(10000);

}catch(InterruptedException e) {// 捕獲中斷異常

? ? ? ? ? ? ? ? ? ? e.printStackTrace();

? ? ? ? ? ? ? ? }

System.out.println("thread end...");

? ? ? ? ? ? }

? ? ? ? };

// 啟動(dòng)線程

? ? ? ? thread.start();


// 主線程等待1秒臭墨,拋出一個(gè)中斷信號(hào)

Thread.sleep(1000);

? ? ? ? thread.interrupt();

? ? }

}

  在上面這段程序中的main方法里創(chuàng)建了一個(gè)線程赔嚎,在該線程的 run 方法中調(diào)用 sleep 函數(shù)將該線程阻塞10s.然后調(diào)用Thread 類的 start 方法啟動(dòng)該線程,該線程剛剛被啟動(dòng)就進(jìn)入阻塞狀態(tài)胧弛。主線程等待1s后調(diào)用thread.interrupt()拋出一個(gè)中斷信號(hào)尤误,在run方法中的catch會(huì)正常捕獲到這個(gè)中斷信號(hào),這樣被阻塞的該線程就會(huì)提前退出阻塞狀態(tài)结缚,不需要等待10s線程thread 就會(huì)被提前終止袄膏。

  上述方法主要針對(duì)當(dāng)前線程調(diào)用了Thread.join()或者 Thread.sleep()等方法而被阻塞時(shí)終止該線程。如果一個(gè)線程被I/O阻塞掺冠,則無(wú)法通過thread.interrupt()拋出一個(gè)中斷信號(hào)而離開阻塞狀態(tài)沉馆。這時(shí)可推而廣之,觸發(fā)一個(gè)與當(dāng)前I/O0阻塞相關(guān)的異常德崭,使其退出I/O阻塞斥黑,然后通過catch 塊捕獲該異常,從而安全地結(jié)束該線程眉厨。

總結(jié)一下:

  當(dāng)一個(gè)線程處于運(yùn)行狀態(tài)時(shí)锌奴,可通過設(shè)置退出標(biāo)志的方法安全結(jié)束該線程;當(dāng)一個(gè)線程被阻塞而無(wú)法正常運(yùn)行時(shí)憾股,可以拋出一個(gè)異常使其退出阻塞狀態(tài)鹿蜀,并 catch 住該異常從而安全結(jié)束該線程。

3|0線程的狀態(tài)(JVM層面)

  我們?cè)谏厦嬗懻摰木€程狀態(tài)是從操作系統(tǒng)層面來(lái)看的服球,這樣看比較直觀茴恰,也容易理解,也是一個(gè)線程在操作系統(tǒng)中真實(shí)狀態(tài)的體現(xiàn)斩熊。下面我們來(lái)看看Java 中線程的狀態(tài)及轉(zhuǎn)換往枣。

3|1Java 線程狀態(tài)

在Java中線程的狀態(tài)有6種,我們來(lái)看一看JDK 1.8幫助文檔中的說明:


JDK1.8幫助文檔-線程狀態(tài)

  我們可以看到幫助文檔中的最后一行粉渠,這些狀態(tài)是不反映任何操作系統(tǒng)線程狀態(tài)的JVM層面的狀態(tài)分冈。我們來(lái)具體看一看這六種狀態(tài):

NEW:初始狀態(tài),線程被創(chuàng)建霸株,但是還沒有調(diào)用 start 方法雕沉。

RUNNABLED:運(yùn)行狀態(tài),JAVA 線程把操作系統(tǒng)中的就緒和運(yùn)行兩種狀態(tài)統(tǒng)稱為“運(yùn)行狀態(tài)”去件。

BLOCKED:阻塞狀態(tài)坡椒,表示線程進(jìn)入等待狀態(tài),也就是線程因?yàn)槟撤N原因放棄了 CPU 使用權(quán)饺著,阻塞也分為幾種情況 :

等待阻塞:運(yùn)行的線程執(zhí)行了 Thread.sleep 、wait()肠牲、 join() 等方法JVM 會(huì)把當(dāng)前線程設(shè)置為等待狀態(tài)幼衰,當(dāng) sleep 結(jié)束、join 線程終止或者wait線程被喚醒后缀雳,該線程從等待狀態(tài)進(jìn)入到阻塞狀態(tài)渡嚣,重新?lián)屨兼i后進(jìn)行線程恢復(fù);

同步阻塞:運(yùn)行的線程在獲取對(duì)象的同步鎖時(shí)肥印,若該同步鎖被其他線程鎖占用了识椰,那么jvm會(huì)把當(dāng)前的線程放入到鎖池中 ;

其他阻塞:發(fā)出了 I/O請(qǐng)求時(shí)深碱,JVM 會(huì)把當(dāng)前線程設(shè)置為阻塞狀態(tài)腹鹉,當(dāng) I/O處理完畢則線程恢復(fù);

WAITING:等待狀態(tài)敷硅,沒有超時(shí)時(shí)間功咒,要被其他線程喚醒或者有其它的中斷操作;

執(zhí)行 wait()

執(zhí)行 join()

執(zhí)行 LockSupport.park()

TIME_WAITING:超時(shí)等待狀態(tài)绞蹦,超時(shí)以后自動(dòng)返回力奋;

執(zhí)行 sleep(long)

執(zhí)行 wait(long)、join(long)

執(zhí)行 LockSupport.parkNanos(long)幽七、LockSupport.parkUntil(long)

TERMINATED:終止?fàn)顟B(tài)景殷,表示當(dāng)前線程執(zhí)行完畢 。

3|2Java 線程狀態(tài)轉(zhuǎn)換

在這借用一下大佬的圖澡屡,因?yàn)檫@張圖畫真的很棒:


4|0?總結(jié)一下

4|1Java 線程的狀態(tài):

操作系統(tǒng)層面:

  有5個(gè)狀態(tài)猿挚,分別是:New(新建)、Runnable(就緒)、Running(運(yùn)行)、Blocked(阻塞)礁苗、Dead(死亡)。

JVM層面:

  有6個(gè)狀態(tài)辜羊,分別是:NEW(新建)、RUNNABLE(運(yùn)行)词顾、BLOCKED(阻塞)、WAITING(等待)碱妆、TIMED_WAITING(超時(shí)等待)肉盹、TERMINATED(終止)。

4|2Java 線程的狀態(tài)控制:

  主要由這幾個(gè)方法來(lái)控制:sleep疹尾、join上忍、yield骤肛、wait、notify以及notifyALL窍蓝。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末腋颠,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子吓笙,更是在濱河造成了極大的恐慌淑玫,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,826評(píng)論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件面睛,死亡現(xiàn)場(chǎng)離奇詭異絮蒿,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)叁鉴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門土涝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人幌墓,你說我怎么就攤上這事但壮。” “怎么了常侣?”我有些...
    開封第一講書人閱讀 164,234評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵茵肃,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我袭祟,道長(zhǎng)验残,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,562評(píng)論 1 293
  • 正文 為了忘掉前任巾乳,我火速辦了婚禮您没,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘胆绊。我一直安慰自己氨鹏,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,611評(píng)論 6 392
  • 文/花漫 我一把揭開白布压状。 她就那樣靜靜地躺著仆抵,像睡著了一般。 火紅的嫁衣襯著肌膚如雪种冬。 梳的紋絲不亂的頭發(fā)上镣丑,一...
    開封第一講書人閱讀 51,482評(píng)論 1 302
  • 那天,我揣著相機(jī)與錄音娱两,去河邊找鬼莺匠。 笑死,一個(gè)胖子當(dāng)著我的面吹牛十兢,可吹牛的內(nèi)容都是我干的趣竣。 我是一名探鬼主播摇庙,決...
    沈念sama閱讀 40,271評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼遥缕!你這毒婦竟也來(lái)了卫袒?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,166評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤单匣,失蹤者是張志新(化名)和其女友劉穎夕凝,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體封孙,經(jīng)...
    沈念sama閱讀 45,608評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡迹冤,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,814評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了虎忌。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片泡徙。...
    茶點(diǎn)故事閱讀 39,926評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖膜蠢,靈堂內(nèi)的尸體忽然破棺而出堪藐,到底是詐尸還是另有隱情,我是刑警寧澤挑围,帶...
    沈念sama閱讀 35,644評(píng)論 5 346
  • 正文 年R本政府宣布礁竞,位于F島的核電站,受9級(jí)特大地震影響杉辙,放射性物質(zhì)發(fā)生泄漏模捂。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,249評(píng)論 3 329
  • 文/蒙蒙 一蜘矢、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧品腹,春花似錦岖食、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至羡鸥,卻和暖如春蔑穴,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背兄春。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工澎剥, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人赶舆。 一個(gè)月前我還...
    沈念sama閱讀 48,063評(píng)論 3 370
  • 正文 我出身青樓哑姚,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親芜茵。 傳聞我的和親對(duì)象是個(gè)殘疾皇子叙量,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,871評(píng)論 2 354