文章首發自個人微信公眾號: 小哈學Java
在 Java 初中級面試中,關於執行緒的生命週期可以說是常客了。本文就針對這個問題,通過圖文並茂的方式詳細說說。
結合上圖,執行緒的生命週期大致可分為以下五種狀態:
- NEW - 新建
- RUNNABLE - 等待被CPU排程
- RUNNING - 正在執行
- BLOCKED - 阻塞
- TERMINATED - 結束
一、NEW 狀態
NEW 狀態表示執行緒被新建的狀態,我們來看一段示例程式碼:
Thread thread = new Thread(() -> System.out.println("Hello, world !"));
複製程式碼
當我們在程式碼中 new
一個 Thread 的時候,就代表著 thread 執行緒處於 NEW 狀態了,但是此時該執行緒還未被作業系統建立出來,只有當我們呼叫了 start()
方法之後,該執行緒才會被建立出來。所以準確來說,NEW 狀態只是執行緒物件的狀態。
NEW 狀態的執行緒能發生哪些狀態轉變?
NEW 狀態的執行緒在呼叫 start()
方法後,進入 RUNNABLE 狀態。
二、RUNNABLE 狀態
當我們在程式碼中顯式的呼叫 start()
方法後,JVM 程式會去建立一個新的執行緒,而此執行緒不會馬上被 CPU 排程執行,進入 RUNNING 狀態,這裡會有一箇中間狀態,就是 RUNNABLE 狀態,你可以理解為等待被 CPU 排程的狀態:
如上圖所示,也就是說被建立的出來的執行緒會從 NEW -> RUNNABLE 狀態,等待 CPU 排程,再大白話一點,就是說這種執行緒具備被執行的資格,但是能否進入進行階段,還得看 CPU 的臉色說話。
RUNNABLE 狀態的執行緒能發生哪些狀態轉變?
RUNNABLE 狀態的執行緒無法直接進入 BLOCKED 狀態和 TERMINATED 狀態的。
很多小夥伴這裡可能有疑問,為啥呢?
只有處在 RUNNING 狀態的執行緒,換句話說,只有獲得 CPU 排程執行權的執行緒才有資格進入 BLOCKED 狀態和 TERMINATED 狀態
PS: RUNNABLE 狀態的執行緒要麼能被轉換成 RUNNING 狀態,要麼被意外終止(如
kill -9 PID
)。
三、RUNNING 狀態
當 CPU 排程發生,並任務佇列中選中了某個 RUNNABLE 執行緒時,該執行緒會進入 RUNNING 執行狀態,並且開始呼叫 run()
方法中邏輯程式碼。
RUNNING 狀態的執行緒能發生哪些狀態轉變?
- 被轉換成 TERMINATED 狀態,比如呼叫
stop()
方法; - 被轉換成 BLOCKED 狀態,比如呼叫了
sleep
,wait
方法被加入waitSet
中; - 被轉換成 BLOCKED 狀態,如進行 IO 阻塞操作,如查詢資料庫進入阻塞狀態;
- 被轉換成 BLOCKED 狀態,比如獲取某個鎖的釋放,而被加入該鎖的阻塞佇列中;
- 該執行緒的時間片用完,CPU 再次排程,進入 RUNNABLE 狀態;
- 執行緒主動呼叫
yield
方法,讓出 CPU 資源,進入 RUNNABLE 狀態;
四、BLOCKED 狀態
上小節中我們已經講到了,進入 BLOCKED 原因,這裡,我們就直接談談 BLOCK 狀態的執行緒能夠發生哪些狀態改變:
- 被轉換成 TERMINATED 狀態,比如呼叫
stop()
方法,或者是 JVM 意外 Crash; - 被轉換成 RUNNABLE 狀態,阻塞時間結束,比如讀取到了資料庫的資料後;
- 完成了指定時間的休眠,進入到 RUNNABLE 狀態;
- 正在
wait
中的執行緒,被其他執行緒呼叫notify/notifyAll
方法喚醒,進入到 RUNNABLE 狀態; - 執行緒獲取到了想要的鎖資源,進入 RUNNABLE 狀態;
- 執行緒在阻塞狀態下被打斷,如其他執行緒呼叫了
interrupt
方法,進入到 RUNNABLE 狀態;
五、TERMINATED 狀態
TERMINATED 狀態是執行緒的最終狀態,處於此狀態中的執行緒不會切換到以上任何狀態,一旦執行緒進入了 TERMINATED 狀態,就意味著這個執行緒生命的終結,沒有回頭路了。
以下情況下,執行緒會進入到 TERMINATED 狀態:
- 執行緒正常執行結束,生命週期結束;
- 執行緒執行過程中出現意外錯誤;
- JVM 異常結束,所有的執行緒生命週期均被結束。
六、start 方法原始碼解析,何時呼叫的 run() 方法?
通過圖文,我們瞭解了執行緒生命週期的五種狀態,接下來,我們來看看 start 方法原始碼,其實內部的原始碼非常簡單,如下圖所示:
- ①:首先,會判斷執行緒的狀態是否是 NEW 狀態,內部對應的狀態標識是個 0,也就是說如果不等於 0,直接拋執行緒狀態異常;
- ②:執行緒在啟動後被加入到
ThreadGroup
中; - ③:
start0
是最核心的方法了,就是執行狀態為 NEW (內部狀態標識為 0) 的執行緒; - ④:
start0
是個native
方法,也就是 JNI 方法;
看到這裡,你也許會有個疑問,自己重寫的 run 方法是什麼時候被呼叫的呢?原始碼中也沒看到呼叫啊!!
Causes this thread to begin execution; the Java Virtual Machine calls the run method of this thread.
上面這段截自 JDK 官方文件,意思是說:
run 方法是在呼叫 JNI 方法 start0() 的時候被呼叫的,被呼叫後,我們寫的邏輯程式碼才得以被執行。
一些面試中,面試官也會經常問到這個問題:執行緒的 start 方法和 run 方法有什麼區別?
相信看完上面的原始碼分析,小夥伴們一定可以原始碼的角度懟回去了!
七、總結
本文中,小哈通過圖文的方式解釋了執行緒的五種狀態,以及各種狀態能夠被轉換的狀態。最後,我們簡單看了一下 start()
內部原始碼,知道了 run()
方法何時被執行的。最後,希望看完本文的小夥伴們能有所收穫,下期見!
八、Ref
- 《Java高併發程式設計詳解》
歡迎關注微信公眾號: 小哈學Java
贈送 | 面試&學習福利資源
最近在網上發現一個不錯的 PDF 資源《Java 核心面試知識.pdf》分享給大家,不光是面試,學習,你都值得擁有!!!
獲取方式: 關注微信公眾號: 小哈學Java, 後臺回覆"資源",既可免費無套路獲取資源連結,下面是目錄以及部分截圖:
重要的事情說兩遍,獲取方式: 關注微信公眾號: 小哈學Java, 後臺回覆"資源",既可免費無套路獲取資源連結 !!!