【Java基礎】:執行緒的生命週期

連江偉發表於2016-08-31

        上篇部落格介紹了建立執行緒的三種方式之間的優劣,我們接著學習多執行緒的生命週期。

        當執行緒被建立並且啟動之後,它既不是一啟動就直接進入了執行狀態,也不是一直處於執行狀態,線上程的生命週期中,它要經過new、runnable、running、blocked和dead五種狀態。尤其是當執行緒啟動以後,它不可能一直“霸佔”著CPU獨自執行,否則多執行緒也就不存在了,這樣一來就需要CPU在多條執行緒之間來回切換,因此執行緒狀態也會多次在執行和就緒這兩者之間切換。

        首先我們看一張執行緒狀態轉換圖,對執行緒的生命週期有一個整體的認識,方便我們接下來對每個狀態的解釋:


        新建狀態

        當程式使用new關鍵字建立一個執行緒之後,該執行緒就處於新建狀態,此時它和其他的java物件一樣,僅僅由java虛擬機器為其分配記憶體,並初始化了其他成員變數的值。此時的執行緒物件沒有表現出任何執行緒的動態特徵,程式也不會執行執行緒執行體中的業務邏輯程式碼。

        就緒狀態

        當執行緒物件呼叫了start方法之後,該執行緒處於就緒狀態,java虛擬機器會為其建立方法呼叫棧和程式計數器,處於這個狀態中的執行緒並沒有開始執行,它只是表示該執行緒可以執行了。至於該執行緒何時開始執行,取決於JVM裡執行緒排程器的排程。

        執行狀態

        如果處於就緒狀態的執行緒獲得了CPU,開始執行run方法的執行緒執行體,則該執行緒處於執行狀態,如果計算機只有一個CPU,在任何時刻只有一條執行緒處於執行狀態。當然,在一個多處理器的機器上,將會有多個執行緒並行執行;但當執行緒數大於處理器數時,依然會有多條執行緒在同一個CPU上輪換的現象。

        阻塞狀態

        當一條執行緒開始執行後,它不可能一直處於執行狀態(除非它的執行緒執行體足夠短,瞬間就執行結束了),執行緒在執行過程中需要被中斷,目的是使其他的執行緒獲得執行的機會,執行緒排程的細節取決於底層平臺所採用的策略。

        那麼執行緒什麼時候會進入阻塞狀態呢?大致有如下幾種情況:

        1執行緒呼叫了sleep方法主動放棄了所佔用的處理器資源。

        2執行緒呼叫了一個阻塞式IO方法,在該方法返回之前,該執行緒被阻塞

        3執行緒試圖獲得一個同步監視器,但該同步監視器正被其他執行緒所持有。

        4執行緒在等待某個通知(notify)。

        5程式呼叫了執行緒的suspend方法將該執行緒掛起。不過這個方法容易導致死鎖,所以程式應該儘量避免使用該方法。

        當前正在執行的執行緒被阻塞之後,其他執行緒就可以獲得執行的機會了。被阻塞的執行緒會在合適的時間重新進入就緒狀態,注意是就緒狀態而不是執行狀態。也就是說被阻塞執行緒的阻塞解除後,必須重新等待執行緒排程器再次排程它。

        因此針對以上造成執行緒進入阻塞狀態的情況,當相應的條件具備之後,同樣會重新進入就緒狀態,比如呼叫sleep方法的執行緒經過了指定的時間,執行緒呼叫的阻塞式IO方法已經返回了,執行緒成功地獲得了試圖取得的同步監視器,正在等待通知的執行緒獲得了通知,處於掛起狀態的執行緒被呼叫了resume恢復方法。

        我們從上面的狀態轉換圖可以看到,執行緒從阻塞狀態只能進入就緒狀態,無法進入執行狀態。而就緒和執行狀態之間的轉換通常不受程式控制,而是由系統排程所導致的。需要注意的是,呼叫yield方法可以讓當前處於執行狀態的執行緒直接進入就緒狀態。

        死亡狀態

        執行緒最終歸宿就是死亡,通常有以下三種方式讓執行緒進入死亡狀態:

        1run方法執行完成,執行緒正常結束

        2執行緒丟擲一個未捕獲的Exception或者Error。

        3直接呼叫該執行緒的stop方法來結束該執行緒——該方法容易導致死鎖,通常不推薦使用。

        判斷某條執行緒是否已經死亡,可以呼叫執行緒物件的isAlive方法,當執行緒處於就緒、執行河阻塞三種狀態時,該方法將返回true;當執行緒處於新建、死亡兩種狀態時,該方法將返回false。對已經死亡的執行緒物件再次呼叫start方法,將會丟擲IllegalThreadStateException異常,這表明死亡狀態的執行緒無法再次執行了。

相關文章