【圖解】透徹Java線程狀態(tài)轉(zhuǎn)換
大家好,我是阿星,好久不見(jiàn),歡迎來(lái)到Java并發(fā)編程系列番外篇線程狀態(tài)轉(zhuǎn)換,內(nèi)容通俗易懂,請(qǐng)放心食用。
線程狀態(tài)
先來(lái)個(gè)開(kāi)場(chǎng)四連問(wèn)
Java線程狀態(tài)有幾個(gè)?Java線程狀態(tài)是如何轉(zhuǎn)換?Java線程狀態(tài)轉(zhuǎn)換什么情況會(huì)進(jìn)入鎖的等待隊(duì)列?Java線程狀態(tài)轉(zhuǎn)換什么情況會(huì)進(jìn)入鎖的同步隊(duì)列?
一提到Java線程狀態(tài),不少讀者立馬想到線程狀態(tài)轉(zhuǎn)換圖,但細(xì)想,印象又不深刻,只記得有那么幾個(gè)狀態(tài)。
再要你說(shuō)出Java線程狀態(tài)是如何轉(zhuǎn)換的,腦子里可能就如下圖一樣,已經(jīng)是一團(tuán)漿糊了。

別慌,阿星為了幫讀者大大們理清楚上面的問(wèn)題,會(huì)一步一步的把線程狀態(tài)轉(zhuǎn)換圖給畫出來(lái),讓讀者大大們真正的理解Java線程狀態(tài)轉(zhuǎn)換。
首先線程的狀態(tài)可以分為6態(tài)或7態(tài),具體狀態(tài)如下
6態(tài)
New:新建狀態(tài)Runnable:可運(yùn)行狀態(tài)Terminated:終止?fàn)顟B(tài)Waiting:等待狀態(tài)TimedWaiting:超時(shí)等待狀態(tài)Blocked:阻塞狀態(tài)
7態(tài)
New:新建狀態(tài)Ready:就緒狀態(tài)Running:運(yùn)行狀態(tài)Terminated:終止?fàn)顟B(tài)Waiting:等待狀態(tài)TimedWaiting:超時(shí)等待狀態(tài)Blocked:阻塞狀態(tài)
其實(shí)6態(tài)與7態(tài)差別不大,只不過(guò)7態(tài)把Runnable可運(yùn)行狀態(tài),拆解成了Ready就緒狀態(tài)與Running運(yùn)行狀態(tài)。
阿星以7態(tài)為例,來(lái)逐步講解它們之間是如何轉(zhuǎn)換的。
新建狀態(tài)(New)
我們可以通過(guò)實(shí)現(xiàn)Runnable接口或繼承Thread聲明一個(gè)線程類,new一個(gè)實(shí)例后,線程就進(jìn)入了新建狀態(tài)。

一個(gè)剛誕生的線程,處于新建狀態(tài)。
就緒狀態(tài)(Ready)
線程對(duì)象創(chuàng)建成功后,調(diào)用該線程的start()函數(shù),線程進(jìn)入就緒狀態(tài),該狀態(tài)的線程進(jìn)入可運(yùn)行線程池中,等待獲取C P U的使用權(quán)。

線程表示,我已經(jīng)準(zhǔn)備好了,此時(shí)我是就緒狀態(tài),快選我吧~
運(yùn)行狀態(tài)(Running)
此時(shí)線程調(diào)度程序正在從可運(yùn)行線程池中選擇一個(gè)線程,該線程進(jìn)入運(yùn)行狀態(tài)。
換句話說(shuō),線程獲取到了C P U時(shí)間片。

還沒(méi)完呢,當(dāng)線程時(shí)間片用完或調(diào)用的yield()函數(shù),該線程回到就緒狀態(tài)。

作為一名運(yùn)氣好的線程,我進(jìn)入了運(yùn)行狀態(tài),但是運(yùn)氣用完了,我還得回到就緒狀態(tài)。
終止?fàn)顟B(tài)(Terminated)
線程繼續(xù)運(yùn)行,直到執(zhí)行結(jié)束或執(zhí)行過(guò)程中因異常意外終止都會(huì)使線程進(jìn)入終止?fàn)顟B(tài)。
線程一旦終止,就不能復(fù)生,這是不可逆的過(guò)程。

線程的人生迎來(lái)了終點(diǎn),可能一帆風(fēng)順過(guò)完一生,也可能英年早逝令人惋惜。
等待狀態(tài)(Waiting)
運(yùn)行狀態(tài)的線程執(zhí)行wait()、join()、LockSupport.park()任意函數(shù),該線程進(jìn)入等待狀態(tài)。
其中wait()與join()函數(shù)會(huì)讓J V M把該線程放入鎖等待隊(duì)列。
處于這種狀態(tài)的線程不會(huì)被分配C P U執(zhí)行時(shí)間,它們要等待被主動(dòng)喚醒,否則會(huì)一直處于等待狀態(tài)。

如果我們要喚醒線程怎么辦呢?
執(zhí)行LockSupport.unpark(t)函數(shù)喚醒指定線程,該線程回到就緒狀態(tài)。
而通過(guò)notify()、notifyAll()、join線程執(zhí)行完畢方式,會(huì)喚醒鎖等待隊(duì)列的線程,出隊(duì)的線程回到就緒狀態(tài)。

線程的人生迎來(lái)了劫難,聽(tīng)信小人之言,跑去菲律賓做打工,結(jié)果被黑工廠扣下,只希望警察叔叔能早日解救我出去。
超時(shí)等待狀態(tài)(Timed waiting)
超時(shí)等待與等待狀態(tài)一樣,唯一的區(qū)別就是多了超時(shí)機(jī)制,不會(huì)一直等待被其他線程主動(dòng)喚醒,而是到達(dá)指定時(shí)間后會(huì)自動(dòng)喚醒。
以下函數(shù)會(huì)觸發(fā)進(jìn)入超時(shí)等待狀態(tài)
wait(long) join(long) LockSupport.parkNanos(long) LockSupport.parkUtil(long) sleep(long)
其中wait(long)、join(long)函數(shù)會(huì)讓J V M把線程放入鎖等待隊(duì)列。

后面的喚醒劇情就和等待狀態(tài)如出一轍,就多了超時(shí)時(shí)間到了,自動(dòng)喚醒的動(dòng)作。

從菲律賓回國(guó)后的線程,也做起了違法的勾當(dāng),最終被警察抓捕,好在只判了5年,熬一熬就可以出來(lái)了。
阻塞狀態(tài)(Blocked)
運(yùn)行狀態(tài)的線程獲取同步鎖失敗或發(fā)出I/O請(qǐng)求,該線程進(jìn)入阻塞狀態(tài)。
如果是獲取同步鎖失敗J V M還會(huì)把該線程放入鎖的同步隊(duì)列。

同步鎖被釋放時(shí),鎖的同步隊(duì)列會(huì)出隊(duì)所有線程,進(jìn)入就緒狀態(tài)。
I/O處理完畢時(shí),該線程重新回到就緒狀態(tài)。

出獄后的線程,后面的人生一直都是磕磕碰碰,就沒(méi)順利過(guò),大家要引以為戒。
小結(jié)
相信讀者大大們跟著阿星的思路來(lái),已經(jīng)掌握了線程狀態(tài)的知識(shí),以后面試官問(wèn)這類問(wèn)題,也可以吊打一番,最后放一張簡(jiǎn)化的線程轉(zhuǎn)換圖為本文畫上句號(hào)。

歷史好文推薦
圖文并茂的聊聊ReentrantReadWriteLock的位運(yùn)算 通俗易懂的ReentrantLock,不懂你來(lái)砍我 萬(wàn)字長(zhǎng)文 | 16張圖解開(kāi)AbstractQueuedSynchronizer 寫給小白看的LockSupport 13張圖,深入理解Synchronized 由淺入深CAS,小白也能與BAT面試官對(duì)線 小白也能看懂的Java內(nèi)存模型 保姆級(jí)教學(xué),22張圖揭開(kāi)ThreadLocal 進(jìn)程、線程與協(xié)程傻傻分不清?一文帶你吃透! 什么是線程安全?一文帶你深入理解
