線程是如何在 6 種狀態(tài)之間轉(zhuǎn)換的?

New 表示線程被創(chuàng)建但尚未啟動(dòng)的狀態(tài):當(dāng)我們用 new Thread() 新建一個(gè)線程時(shí)榛了,如果線程沒(méi)有開始運(yùn)行 start() 方法玉锌,所以也沒(méi)有開始執(zhí)行 run() 方法里面的代碼,那么此時(shí)它的狀態(tài)就是 New瓮顽。而一旦線程調(diào)用了 start()县好,它的狀態(tài)就會(huì)從 New 變成 Runnable。

Java 中的 Runable 狀態(tài)對(duì)應(yīng)操作系統(tǒng)線程狀態(tài)中的兩種狀態(tài)暖混,分別是 Running 和 Ready缕贡,也就是說(shuō),Java 中處于 Runnable 狀態(tài)的線程有可能正在執(zhí)行,也有可能沒(méi)有正在執(zhí)行晾咪,正在等待被分配 CPU 資源收擦。

所以,如果一個(gè)正在運(yùn)行的線程是 Runnable 狀態(tài)谍倦,當(dāng)它運(yùn)行到任務(wù)的一半時(shí)塞赂,執(zhí)行該線程的 CPU 被調(diào)度去做其他事情,導(dǎo)致該線程暫時(shí)不運(yùn)行剂跟,它的狀態(tài)依然不變减途,還是 Runnable,因?yàn)樗锌赡茈S時(shí)被調(diào)度回來(lái)繼續(xù)執(zhí)行任務(wù)曹洽。

在 Java 中阻塞狀態(tài)通常不僅僅是 Blocked鳍置,實(shí)際上它包括三種狀態(tài),分別是 Blocked(被阻塞)送淆、Waiting(等待)税产、Timed Waiting(計(jì)時(shí)等待),這三 種狀態(tài)統(tǒng)稱為阻塞狀態(tài)偷崩,下面我們來(lái)看看這三種狀態(tài)具體是什么含義辟拷。

首先來(lái)看最簡(jiǎn)單的 Blocked,從箭頭的流轉(zhuǎn)方向可以看出阐斜,從 Runnable 狀態(tài)進(jìn)入 Blocked 狀態(tài)只有一種可能衫冻,就是進(jìn)入 synchronized 保護(hù)的代碼時(shí)沒(méi)有搶到 monitor 鎖,無(wú)論是 進(jìn)入 synchronized 代碼塊谒出,還是 synchronized 方法隅俘,都是一樣

當(dāng)處于 Blocked 的線程搶到 monitor 鎖笤喳,就會(huì)從 Blocked 狀態(tài)回到 Runnable 狀態(tài)为居。

線程進(jìn)入 Waiting 狀態(tài)有三種可能性:

  • 沒(méi)有設(shè)置 Timeout 參數(shù)的 Object.wait() 方法。

  • 沒(méi)有設(shè)置 Timeout 參數(shù)的 Thread.join() 方法杀狡。

  • LockSupport.park() 方法蒙畴。

Blocked 僅僅針對(duì) synchronized monitor 鎖,可是在 Java 中還有很多其他的鎖呜象,比如 ReentrantLock膳凝,如果線程在獲取這種鎖時(shí)沒(méi)有搶到該鎖就會(huì)進(jìn)入 Waiting 狀態(tài),因?yàn)楸举|(zhì)上它執(zhí)行了 LockSupport.park() 方法恭陡,所以會(huì)進(jìn)入 Waiting 狀態(tài)鸠项。同樣,Object.wait() 和 Thread.join() 也會(huì)讓線程進(jìn)入 Waiting 狀態(tài)子姜。

Blocked 與 Waiting 的區(qū)別是 Blocked 在 等待其他線程釋放 monitor 鎖,而 Waiting 則是在等待某個(gè)條件,比如 join 的線程執(zhí)行完畢哥捕,或者是 notify()/notifyAll() 牧抽。

在 Waiting 上面是 Timed Waiting 狀態(tài),這兩個(gè)狀態(tài)是非常相似的遥赚,區(qū)別僅在于有沒(méi)有時(shí)間限制扬舒,Timed Waiting 如果等待超時(shí),由系統(tǒng)自動(dòng)喚醒凫佛,或者在超時(shí)前被喚醒信號(hào)喚醒讲坎。

以下情況會(huì)讓線程進(jìn)入 Timed Waiting 狀態(tài):

1,設(shè)置了時(shí)間參數(shù)的 Thread.sleep(long millis) 方法愧薛;

2晨炕,設(shè)置了時(shí)間參數(shù)的 Object.wait(long timeout) 方法;

3毫炉,設(shè)置了時(shí)間參數(shù)的 Thread.join(long millis) 方法瓮栗;

4,設(shè)置了時(shí)間參數(shù)的 LockSupport.parkNanos(long nanos) 方法和 LockSupport.parkUntil(long deadline) 方法瞄勾。

我們?cè)賮?lái)看下如何在這三種狀態(tài)之間進(jìn)行一個(gè)切換:

想要從 Blocked 狀態(tài)進(jìn)入 Runnable 狀態(tài)费奸,要求線程獲取 monitor 鎖,而從 Waiting 狀態(tài)流轉(zhuǎn)到其他狀態(tài)則比較特殊进陡,因?yàn)槭紫?Waiting 是不限時(shí)的愿阐,也就是說(shuō)無(wú)論過(guò)了多長(zhǎng)時(shí)間它都不會(huì) 主動(dòng)恢復(fù),只有當(dāng)執(zhí)行了 LockSupport.unpark()趾疚,或者 join 的線程運(yùn)行結(jié)束缨历,或者被中斷時(shí)才可以進(jìn)入 Runnable 狀態(tài)。

如果其他線程調(diào)用 notify() 或 notifyAll() 來(lái)喚醒它盗蟆,它會(huì)直接進(jìn)入 Blocked 狀態(tài)戈二,這是為什么呢?

喚醒 Waiting 線程的線程如果調(diào)用 notify() 或 notifyAll()喳资,要求它必須先持有 monitor 鎖觉吭,如果處于 Waiting 狀態(tài)的線程被喚醒時(shí)拿不到該鎖,** Waiting 狀態(tài)的線程** 就會(huì)進(jìn)入 Blocked 狀態(tài)仆邓,直到 喚醒它的線程 的 notify()/notifyAll() 執(zhí)行完畢并釋放 monitor 鎖鲜滩,才可能輪到它去搶奪這把鎖,如果它能搶到节值,就會(huì)從 Blocked 狀態(tài)回到 Runnable 狀態(tài)徙硅。

同樣在 Timed Waiting 中執(zhí)行 notify() 和 notifyAll() 也是一樣的道理,它們會(huì)先進(jìn)入 Blocked 狀態(tài)搞疗,然后搶奪鎖成功后嗓蘑,再回到 Runnable 狀態(tài)。

當(dāng)然對(duì)于 Timed Waiting 而言,如果它的超時(shí)時(shí)間到了且能直接獲取到鎖/join的線程運(yùn)行結(jié)束/被中斷/調(diào)用了LockSupport.unpark()桩皿,會(huì)直接恢復(fù)到 Runnable 狀態(tài)豌汇,而無(wú)需經(jīng)歷 Blocked 狀態(tài)。

再來(lái)看看最后一種狀態(tài)泄隔,Terminated 終止?fàn)顟B(tài)拒贱,要想進(jìn)入這個(gè)狀態(tài)有兩種可能:

  • run() 方法執(zhí)行完畢,線程正常退出佛嬉。

  • 出現(xiàn)一個(gè)沒(méi)有捕獲的異常逻澳,終止了 run() 方法,最終導(dǎo)致意外終止暖呕。

最后我們?cè)倏淳€程狀態(tài)轉(zhuǎn)換的兩個(gè)注意點(diǎn):

  • 線程從 New 狀態(tài)是不可以直接進(jìn)入 Blocked 狀態(tài)的斜做,它需要先經(jīng)歷 Runnable 狀態(tài)。

  • 線程生命周期不可逆:一旦進(jìn)入 Runnable 狀態(tài)就不能回到 New 狀態(tài)缰揪;一旦被終止就不可能再有任何狀態(tài)的變化陨享。所以一個(gè)線程只能有一次 New 和 Terminated 狀態(tài),只有處于中間狀態(tài)才可以相互轉(zhuǎn)換钝腺。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末抛姑,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子艳狐,更是在濱河造成了極大的恐慌定硝,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,914評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件毫目,死亡現(xiàn)場(chǎng)離奇詭異蔬啡,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)镀虐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門箱蟆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人刮便,你說(shuō)我怎么就攤上這事空猜。” “怎么了恨旱?”我有些...
    開封第一講書人閱讀 156,531評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵辈毯,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我搜贤,道長(zhǎng)谆沃,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,309評(píng)論 1 282
  • 正文 為了忘掉前任仪芒,我火速辦了婚禮唁影,結(jié)果婚禮上耕陷,老公的妹妹穿的比我還像新娘。我一直安慰自己据沈,他們只是感情好啃炸,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著卓舵,像睡著了一般。 火紅的嫁衣襯著肌膚如雪膀钠。 梳的紋絲不亂的頭發(fā)上掏湾,一...
    開封第一講書人閱讀 49,730評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音肿嘲,去河邊找鬼融击。 笑死,一個(gè)胖子當(dāng)著我的面吹牛雳窟,可吹牛的內(nèi)容都是我干的尊浪。 我是一名探鬼主播,決...
    沈念sama閱讀 38,882評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼封救,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼拇涤!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起誉结,我...
    開封第一講書人閱讀 37,643評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤鹅士,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后惩坑,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體掉盅,經(jīng)...
    沈念sama閱讀 44,095評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評(píng)論 2 325
  • 正文 我和宋清朗相戀三年以舒,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了趾痘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,566評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡蔓钟,死狀恐怖永票,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情奋刽,我是刑警寧澤瓦侮,帶...
    沈念sama閱讀 34,253評(píng)論 4 328
  • 正文 年R本政府宣布,位于F島的核電站佣谐,受9級(jí)特大地震影響肚吏,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜狭魂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評(píng)論 3 312
  • 文/蒙蒙 一罚攀、第九天 我趴在偏房一處隱蔽的房頂上張望党觅。 院中可真熱鬧,春花似錦斋泄、人聲如沸杯瞻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)魁莉。三九已至,卻和暖如春募胃,著一層夾襖步出監(jiān)牢的瞬間旗唁,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評(píng)論 1 264
  • 我被黑心中介騙來(lái)泰國(guó)打工痹束, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留检疫,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,248評(píng)論 2 360
  • 正文 我出身青樓祷嘶,卻偏偏與公主長(zhǎng)得像屎媳,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子论巍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容