線程生命週期
現代操作系統在運行一個程序時,會爲其創建一個進程。例如,啓動一個Java程序,操作系統就會創建一個Java進程。現代操作系統調度的最小單元是線程,也叫輕量級進程(Light Weight Process),在一個進程裏可以創建多個線程,這些線程都擁有各自的計數器、堆棧和局部變量等屬性,並且能夠訪問共享的內存變量。處理器在這些線程上高速切換,讓使用者感覺到這些線程在同時執行。
CPU再切換線程是會導致線程存在各種狀態,線程從創建到死亡其中存在不同的生命狀態;本文將對線程生命週期進行全面的介紹。
Java線程的狀態可以從java.lang.Thread
的內部枚舉類java.lang.Thread$State
得知:
public enum State {
NEW,
RUNNABLE,
BLOCKED,
WAITING,
TIMED_WAITING,
TERMINATED;
}
當線程被創建並啓動以後,它既不是一啓動就進入了執行狀態,也不是一直處於執行狀態。在線程的生命週期中,它要經過 新建(New)、就緒(Runnable)、運行(Running)、阻塞(Blocked、WATING、TIMED_WATING)和死亡(Dead)5種狀態。尤其是當線程啓動以後,它不可能一直"霸佔"着CPU獨自運行,所以CPU需要在多條線程之間切換,於是 線程狀態也會多次在運行、阻塞之間切換。
-
新建狀態(New)
API註釋:
/** * Thread state for a thread which has not yet started. * 線程實例尚未啓動時候的線程狀態 */ NEW,
當線程對象創建後,即進入新建狀態,如:
Thread t = new MyThread();
-
就緒狀態(Runnable)
API註釋:
/** * Thread state for a runnable thread. A thread in the runnable * state is executing in the Java virtual machine but it may * be waiting for other resources from the operating system * such as processor. */ RUNNABLE, 可運行狀態下線程的線程狀態。可運行狀態下的線程在Java虛擬機中執行,但它可能執行等待操作系統的其他資源,例如處理器。
當調用線程對象的
start()
方法時,線程即進入就緒狀態。處於就緒狀態的線程只是說明此線程已經做好準備,隨時等待CPU調度執行,並不是說執行了start()
方法就立即執行。當Java線程實例調用了
Thread#start()
之後,就會進入RUNNABLE
狀態。RUNNABLE
狀態可以認爲包含兩個子狀態:READY
和RUNNING
。READY
:該狀態的線程可以被線程調度器進行調度使之更變爲RUNNING
狀態。RUNNING
:該狀態表示線程正在運行,線程對象的run()
方法中的代碼所對應的的指令正在被CPU執行。- 當Java線程實例
Thread#yield()
方法被調用時或者由於線程調度器的調度,線程實例的狀態有可能由RUNNING
轉變爲READY
,但是從線程狀態Thread#getState()
獲取到的狀態依然是RUNNABLE
。例如: -
public class ThreadState1 { public static void main(String[] args) throws Exception { Thread thread = new Thread(()-> { while (true){ Thread.yield(); } }); thread.start(); Thread.sleep(2000); System.out.println(thread.getState()); } } // 輸出結果 RUNNABLE
-
運行狀態(Running)
當CPU開始調度處於就緒狀態的線程時,此時線程才得以真正執行,即進入到運行狀態。
- WATING狀態
WAITING
是無限期的等待狀態,這種狀態下的線程不會被分配CPU執行時間。當一個線程執行了某些方法之後就會進入無限期等待狀態,直到被顯式喚醒,被喚醒後,線程狀態由WAITING
更變爲RUNNABLE
然後繼續執行。
- TIMED WATING狀態
TIMED WAITING
就是有限期等待狀態,它和WAITING
有點相似,這種狀態下的線程不會被分配CPU執行時間,不過這種狀態下的線程不需要被顯式喚醒,只需要等待超時限期到達就會被VM喚醒,有點類似於現實生活中的鬧鐘。
- 阻塞狀態(Blocked)
處於運行狀態中的線程由於某種原因,暫時放棄對CPU的使用權,停止執行,此時進入阻塞狀態,直到其進入到就緒狀態,纔有機會再次被CPU調用以進入到運行狀態。
阻塞狀態分類
- 等待阻塞:運行狀態中的線程執行wait()方法,使本線程進入到等待阻塞狀態;
- 同步阻塞:線程在獲取
synchronized
同步鎖失敗(因爲鎖被其它線程佔用),它會進入到同步阻塞狀態;- 其他阻塞:通過調用線程的sleep()或join()或發出I/O請求時,線程會進入到阻塞狀態。當
sleep()
狀態超時、join()
等待線程終止或者超時、或者I/O處理完畢時,線程重新轉入就緒狀態。
-
死亡狀態
線程執行完畢或者是異常退出,該線程結束生命週期。
線程相關方法
public class Thread{
// 線程的啓動
public void start();
// 線程體
public void run();
// 已廢棄
public void stop();
// 已廢棄
public void resume();
// 已廢棄
public void suspend();
// 在指定的毫秒數內讓當前正在執行的線程休眠
public static void sleep(long millis);
// 同上,增加了納秒參數
public static void sleep(long millis, int nanos);
// 測試線程是否處於活動狀態
public boolean isAlive();
// 中斷線程 - 進入阻塞狀態
public void interrupt();
// 測試線程是否已經中斷
public boolean isInterrupted();
// 測試當前線程是否已經中斷
public static boolean interrupted();
// 等待該線程終止
public void join() throws InterruptedException;
// 等待該線程終止的時間最長爲 millis 毫秒
public void join(long millis) throws InterruptedException;
// 等待該線程終止的時間最長爲 millis 毫秒 + nanos 納秒
public void join(long millis, int nanos) throws InterruptedException;
}