Java 解析線程的幾種狀態詳解

1. 線程的5種狀態

從操作系統層面上,任何線程一般都具有五種狀態,即創建、就緒、運行、阻塞、終止。

(1) 新建狀態(NEW)

在程序中用構造方法創建一個新線程時,如new Thread(),該線程就是創建狀態,此時它已經有瞭相應的內存空間和其它資源,但是還沒有開始執行。

(2) 就緒狀態(READ)

新建線程對象後,調用該線程的start()方法就可以啟動線程。當線程啟動時,線程就進入就緒狀態(runnable)
由於還沒有分配CPU,線程將進入線程隊列排隊,等待CPU服務,這表明它已經具備瞭運行條件。當系統挑選一個等待執行的Thread對象後,它就會從等待狀態進入執行狀態。系統挑選的動作稱之為“CPU調度”。一旦獲得CPU,線程就進入運行狀態並自動調用自己的run方法。

(3) 運行狀態(RUNNING)

當就緒狀態的線程被調用並獲得處理器資源時,線程就進入瞭運行狀態。此時,自動調用該線程對象的run()方法。

(4) 阻塞狀態( BLOCKED)

一個正在執行的線程在某些特殊情況下,如被人為掛起或需要執行耗時的輸入輸出操作時,將讓出CPU並暫時中止自己的執行,進入堵塞狀態。

在可執行狀態下,如果調用sleep()、suspend()、wait()等方法,線程都將進入阻塞狀態。阻塞時,線程不能進入排隊隊列,隻能當引起阻塞的原因被消除後,線程轉入就緒狀態,重新到就緒隊列中排隊等待,這時被CPU調度選中後會從原來停止的位置開始繼續執行。

記住:阻塞被消除後是回到就緒狀態,不是運行狀態。

(5) 死亡狀態(TERMINATED)

線程調用stop(), destory()或run()執行結束後,線程即處於死亡狀態。處於死亡狀態的線程不具有繼續運行的能力。

2. Java線程的6種狀態

Java中線程的生命周期分為6種狀態。Thread類有一個實例屬性和一個實例方法專門用於保存和獲取線程的狀態。其中,用於保存線程Thread實例狀態的實例屬性為:

// 以整數的形式保存線程的狀態
private volatile int threadStatus = 0;
// 返回當前線程的狀態,一個枚舉類型值
public State getState() {
    return sun.misc.VM.toThreadState(threadStatus);
}

Thread.State是一個內部枚舉類,定義瞭6個枚舉常量,分別代表Java線程的6種狀態,具體如下:

public enum State {
    // 新建狀態
    NEW,
    // 運行狀態
    RUNNABLE,
    /**
     * 阻塞狀態
     * Object.wait
     */
    BLOCKED,
    /**
     *  等待狀態
     *  Object.wait
     *  Thread.join
     *  LockSupport.park
     */
    WAITING,
    /**
     *  限時等待狀態
     *  Thread.sleep
     *  Object.wait
     *  Thread.join
     *  LockSupport.parkUntil
     *  LockSupport.parkNanos
     */
    TIMED_WAITING,
    // 終止狀態
    TERMINATED;
}

有4種是比較常見的狀態,它們是:NEW(新建)狀態、RUNNABLE(可執行)狀態、TERMINATED(終止)狀態、TIMED_WAITING(限時等待)狀態。

(1) NEW狀態

Java源碼對NEW狀態的說明是:創建成功但是沒有調用start()方法啟動的Thread線程實例都處於NEW狀態。

當然,並不是Thread線程實例的start()方法一經調用,其狀態就從NEW狀態到RUNNABLE狀態,此時並不意味著線程立即獲取CPU時間片並且立即執行,中間需要一系列操作系統的內部操作。

(2) RUNNABLE狀態

當調用瞭Thread實例的start()方法後,下一步如果線程獲取CPU時間片開始執行,JVM將異步調用線程的run()方法執行其業務代碼。那麼在run()方法被異步調用之前,JVM做瞭哪些事情呢?當Java線程的Thread實例的start()方法被調用後,操作系統中的對應線程進入的並不是運行狀態,而是就緒狀態,而Java線程並沒有這個就緒狀態。操作系統中線程的就緒狀態是什麼狀態的呢?JVM的線程狀態與其幕後的操作系統線程狀態之間的轉換關系簡化後如圖:

在這裡插入圖片描述

一個操作系統線程如果處於就緒狀態,就表示“萬事俱備,隻欠東風”,即該線程已經滿足執行條件,但是還不能執行。處於就緒狀態的線程需要等待系統的調度,一旦就緒狀態被系統選中,獲得CPU時間片,線程就開始占用CPU,開始執行線程的代碼,這時線程的操作系統狀態發生瞭改變,進入瞭運行狀態。

在操作系統中,處於運行狀態的線程在CPU時間片用完之後,又回到就緒狀態,等待CPU的下一次調度。就這樣,操作系統線程在就緒狀態和執行狀態之間被系統反復地調度,這種情況會一直持續,直到線程的代碼邏輯執行完成或者異常終止。這時線程的操作系統狀態又發生瞭改變,進入線程的最後狀態——TERMINATED狀態。

就緒狀態和運行狀態都是操作系統中的線程狀態。在Java語言中,並沒有細分這兩種狀態,而是將這兩種狀態合並成同一種狀態——RUNNABLE狀態。因此,在Thread.State枚舉類中,沒有定義線程的就緒狀態和運行狀態,隻是定義瞭RUNNABLE狀態。這就是Java線程狀態和操作系統中線程狀態不同的地方。

總之,NEW狀態的Thread實例調用瞭start()方法後,線程的狀態將變成RUNNABLE狀態。盡管如此,線程的run()方法不一定會馬上被並發執行,需要在線程獲取瞭CPU時間片之後才真正啟動並發執行。

(3) TERMINATED狀態

處於RUNNABLE狀態的線程在run()方法執行完成之後就變成終止狀態TERMINATED瞭。當然,如果在run()方法執行過程中發生瞭運行時異常而沒有被捕獲,run()方法將被異常終止,線程也會變成TERMINATED狀態。

(4) TIMED_WAITING狀態

線程處於一種特殊的等待狀態,準確地說,線程處於限時等待狀態。能讓線程處於限時等待狀態的操作大致有以下幾種:

  • Thread.sleep(int n):使得當前線程進入限時等待狀態,等待時間為n毫秒。
  • Object.wait():帶時限的搶占對象的monitor鎖。
  • Thread.join():帶時限的線程合並。
  • LockSupport.parkNanos():讓線程等待,時間以納秒為單位。
  • LockSupport.parkUntil():讓線程等待,時間可以靈活設置。

3. Java線程狀態的轉換

在這裡插入圖片描述

總結

本篇文章就到這裡瞭,希望能夠給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容!      

推薦閱讀: