Java多線程生命週期及狀態轉換

進程與線程的區別

進程: 一個任務就是一個進程,當一個程序進入內存運行時,即變成一個進程。進程是處於運行過程中的程序,並具有一定的獨立功能,是系統進行資源分配和調度的一個獨立單元
線程: 就是進程中的一個獨立的控制單元,是進程中執行運算的最小單位,也是調度運行的基本單位
一個進程中至少有一個線程

舉個例子:

計算機上的任務管理器,會顯示出當前機器的所有進程,QQ,微信等,當QQ或微信運行時,就有很多子任務在同時運行。比如,邊打字發送表情、邊好友視頻這些不同的功能都可以同時運行,其中每一項任務都可以理解成“線程”在工作

一個線程的生命週期

線程是一個動態執行的過程,它也有一個從產生到死亡的過程
線程狀態
新建: 當程序使用 new 關鍵字創建一個線程之後,該線程就處於新建狀態,此時線程對象沒有表現出任何線程的動態特徵
就緒: 當線程對象調用了 start() 方法後,該線程處於就緒狀態。java虛擬機會爲其創建方法調用棧和程序計數器,處於這個狀態中的線程並沒有開始運行。只能對處於新建狀態的線程調用 start() 方法,否則將引發異常
運行: 如果處於就緒狀態的線程獲得了CPU執行權,開始執行 run() 方法的線程執行體,則該線程處於運行狀態
阻塞: 當發生以下情況時,線程將會進入阻塞狀態:
            ①線程調用sleep()方法主動放棄所佔用的處理器資源
            ②調用一個阻塞式IO方法,在該方法返回之前
            ③線程試圖獲得一個同步監視器,但該同步監視器正被其他線程所持有
            ④線程在等待某個通知
            ⑤程序調用線程的 suspend() 方法將該線程掛起,但這個方法容易產生死鎖,已棄用
            線程從阻塞狀態只能進入就緒狀態,無法直接進入運行狀態,而就緒和運行狀態之間轉換通常不受程序控制,而是由系統線程調度所決定,可使用 yield() 方法讓運行狀態的線程轉入就緒狀態
死亡: 以下情況時,線程將會進入死亡狀態:
            ①run() 方法或call() 方法執行完成,線程正常結束
            ②線程拋出一個未捕獲的Exception或Error
            ③直接調用該線程的stop() 方法結束線程,但是該方法不安全、容易導致死鎖,已棄用
            ④調用 interrupt() 方法

Java中線程的生命週期

NEW(初始化狀態)
RUNNABLE(可運行 / 運行狀態)
BLOCKED(阻塞狀態)
WAITING(無時限等待)
TIMED_WAITING(有時限等待)
TERMINATED(終止狀態)
在操作系統層面,Java 線程中的 BLOCKED、WAITING、TIMED_WAITING 是一種狀態,即阻塞狀態。也就是說只要 Java 線程處於這三種狀態之一,那麼這個線程就永遠沒有 CPU 的使用權

線程狀態轉換

RUNNABLE 與 BLOCKED 的狀態轉換:

當線程等待 synchronized 的隱式鎖時,會觸發這種轉換。synchronized 修飾的方法、代碼塊同一時刻只允許一個線程執行,其他線程只能等待,這種情況下,等待的線程就會從 RUNNABLE 轉換到 BLOCKED 狀態。而當等待的線程獲得 synchronized 隱式鎖時,就又會從 BLOCKED 轉換到 RUNNABLE 狀態。
在操作系統層面,線程會轉換到休眠狀態,但在 JVM 層面,Java 線程的狀態不會發生變化,也就是說 Java 線程的狀態依然是 RUNNABLE 狀態。JVM 層面並不關心操作系統調度相關狀態,因爲在 JVM 看來,等待 CPU 使用權(操作系統層面此時處於可執行狀態)與等待 I/O(操作系統層面此時處於休眠狀態)沒有區別,都是在等待某個資源,所以都歸入了 RUNNABLE 狀態。平時所謂的 Java 在調用阻塞式 API 時,線程會阻塞,其實指的是操作系統線程的狀態,並不是 Java 線程的狀態。

RUNNABLE 與 WAITING 的狀態轉換:

1、獲得 synchronized 隱式鎖的線程調用了無參數的 Object.wait() 方法
2、調用無參數的 Thread.join() 方法。其中的 join() 是一種線程同步方法,例如有一個線程對象 thread A,當調用 A.join() 的時候,執行這條語句的線程會等待 thread A 執行完,而等待中的這個線程狀態會從 RUNNABLE 轉換到 WAITING。當線程 thread A 執行完,原來等待它的線程又會從 WAITING 狀態轉換到 RUNNABLE。
3、Java 併發包中的鎖,都是基於LockSupport實現的。調用 LockSupport.park() 方法,當前線程會阻塞,線程的狀態會從 RUNNABLE 轉換到 WAITING。調用 LockSupport.unpark(Thread thread) 可喚醒目標線程,目標線程的狀態又會從 WAITING 狀態轉換到 RUNNABLE。

RUNNABLE 與 TIMED_WAITING 的狀態轉換:

1、調用 Thread.sleep(long millis) 方法
2、調用 Thread.join(long millis) 方法
3、調用 LockSupport.parkNanos(Object blocker, long deadline) 方法
4、調用 LockSupport.parkUntil(long deadline) 方法
5、獲得 synchronized 隱式鎖的線程,調用Object.wait(long timeout) 方法

從 NEW 到 RUNNABLE 狀態:

調用線程對象的 start() 方法

從 RUNNABLE 到 TERMINATED 狀態:

1、run() 方法或call() 方法執行完成,線程正常結束
2、線程拋出一個未捕獲的Exception或Error
3、直接調用該線程的stop() 方法結束線程,但是該方法不安全、容易導致死鎖,已棄用
4、調用 interrupt() 方法

線程的優先級

每一個 Java 線程都有一個優先級,這樣有助於操作系統確定線程的調度順序。
Java 線程的優先級是一個整數,其取值範圍是 1 (Thread.MIN_PRIORITY ) ~ 10(Thread.MAX_PRIORITY )。默認情況下,每一個線程都會分配一個優先級 NORM_PRIORITY(5)。
具有較高優先級的線程對程序更重要,並且應該在低優先級的線程之前分配處理器資源。但是,線程優先級不能保證線程執行的順序,而且非常依賴於平臺。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章