當線程被創建並啓動以後,它既不是已啓動就進入執行狀態,也不是一直處於執行狀態,在線程的生命週期中,它要經過新建(New)、就緒(Runnable)、運行(Running)、阻塞(Blocked)和死亡(Dead)5中狀態。尤其是當線程啓動以後,它不可能一直“霸佔”着CPU獨自運行(如果在一個cpu分片時間之內執行完,那就不會切換),所以CPU需要在多條線程之間切換(可以查看cpu多線程實現原理),於是線程狀態也會多次在運行、就緒之間切換。
1.新建和就緒狀態
當程序使用new關鍵字創建了一個線程之後,該線程就處於新建狀態,此時它和其他的java對象一樣,僅僅由java虛擬機爲其分配內存,並初始化成員變量的值。此時的線程對象沒有表現出任何線程的動態特徵,程序也不會執行線程的線程執行體。
當線程對象調用了start()方法後,該線程處於就緒狀態,java虛擬機會爲其創建方法調用棧和程序計數器,處於這個狀態中的線程並沒有開始運行,只是表示該線程可以運行了。
2.運行和阻塞狀態
如果處於就緒狀態的線程獲得了CPU,開始執行run()方法的線程執行體,則該線程處於運行狀態。如果計算機只要一個CPU,那麼在任何時刻只有一個線程處於運行狀態。在一個多處理器的機器上,將會有多個線程並行(是並行,不是併發:parallel)執行;當線程數大於處理器數時,依然會存在多個線程在同一CPU上輪換的現象(線程調度的策略取決於底層操作系統,目前所有現代的桌面和服務器操作系統都採用搶佔式策略)。
對於採用搶佔式策略的系統,系統會給每個可執行的線程一小個時間段來處理任務,當該時間段用完後,如果線程還沒有執行完,系統就會剝奪該線程所佔用的資源,讓其他線程獲得執行的機會。在選着下一個線程時,系統會考慮線程的優先級。
當發生如下情況下時,線程將會進入阻塞狀態:
1.線程調用sleep()方法,主動放棄所佔用的處理器資源
2.線程調用了一個阻塞式IO方法,在該方法返回之前,該線程被阻塞。
3.線程視圖獲取一個同步監視器,但該同步監視器正被其他線程所持有。
4.線程等待某個通知(notify)。
5.程序調用了線程的suspend()方法將該線程掛起。這個方法容易導致死鎖,所以應該儘量避免使用。
當前正在執行的線程被阻塞之後,其他線程就可以獲得執行的機會。被阻塞的線程會在合適的時候重新進入就緒狀態(是就緒狀態,不是運行狀態)。也就是說,被阻塞線程的阻塞解除後,必須重新等待線程調度器的再次調用。
線程狀態轉換圖如下所示:
線程從阻塞狀態只能進入就緒狀態,無法直接進入運行狀態。而就緒和運行狀態之間的轉換通常不受程序控制,而是由操作系統調度所決定。當處於就緒狀態的線程獲得處理器資源時,該線程進入運行狀態,當處於運行狀態的線程失去處理器資源時,該線程進入就緒狀態。但有一個方法例外,調用yield()方法可以讓處於運行狀態的線程轉入就緒(yield()方法將在後面介紹)。
3.線程死亡
線程會以如下三種方式結束,結束後就處於死亡狀態。
1.run()或call()方法執行完成,線程正常結束
2.線程拋出一個未被捕獲的Exception或Error。
3.直接調用該線程的stop()方法來結束線程---該方法容易導致死鎖,通常不建議使用。
注:當主線程結束時,其他線程不受任何影響,並不會隨之結束。一旦子線程啓動起來後,它就擁有和主線程相同的地位,它不 受主線程的影響。
參考來源:《java瘋狂講義》