溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java中線程的生命周期是什么

發布時間:2022-01-07 16:38:04 來源:億速云 閱讀:187 作者:iii 欄目:編程語言

這篇文章主要介紹“ Java中線程的生命周期是什么”的相關知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“ Java中線程的生命周期是什么”文章能幫助大家解決問題。

操作系統中線程的生命周期

操作系統的線程生命周期基本上可以用下圖這個五態模型來描述。這五態分別是:初始狀態、可運行狀態、運行狀態、休眠狀態和終止狀態。

Java中線程的生命周期是什么

這五態模型的詳細情況如下所示。

1.初始狀態:指的是線程已經被創建,但是還不允許分配 CPU  執行。這個狀態屬于編程語言特有的,不過這里所謂的被創建,僅僅是在編程語言層面被創建,而在操作系統層面,真正的線程還沒有創建。

2.可運行狀態:指的是線程可以分配 CPU 執行。在這種狀態下,真正的操作系統線程已經被成功創建了,所以可以分配 CPU 執行。

3.當有空閑的 CPU 時,操作系統會將其分配給一個處于可運行狀態的線程,被分配到 CPU 的線程的狀態就轉換成了運行狀態。

4.運行狀態的線程如果調用一個阻塞的 API(例如以阻塞方式讀文件)或者等待某個事件(例如條件變量),那么線程的狀態就會轉換到休眠狀態,同時釋放 CPU  使用權,休眠狀態的線程永遠沒有機會獲得 CPU 使用權。當等待的事件出現了,線程就會從休眠狀態轉換到可運行狀態。

5.線程執行完或者出現異常就會進入終止狀態,終止狀態的線程不會切換到其他任何狀態,進入終止狀態也就意味著線程的生命周期結束了。

Java 中把可運行狀態和運行狀態合并了,這兩個狀態在操作系統調度層面有用,而 JVM 層面不關心這兩個狀態,因為 JVM  把線程調度交給操作系統處理了,Java 中還細化了休眠狀態等。

Java 中線程的生命周期

接下來來看看 Java 中線程的生命周期,Java 中線程共有六種狀態,分別是:

1.NEW(初始化狀態)

2.RUNNABLE(可運行 / 運行狀態)

3.BLOCKED(阻塞狀態)

4.WAITING(無時限等待)

5.TIMED_WAITING(有時限等待)

6.TERMINATED(終止狀態)

在操作系統層面,Java 線程中的 BLOCKED(阻塞狀態)、WAITING(無時限等待)、TIMED_WAITING(有時限等待)  是一種狀態,即休眠狀態。也就是說只要 Java 線程處于這三種狀態之一,那么這個線程就永遠沒有 CPU 的使用權。

所以 Java 線程的生命周期可以簡化為下圖:

Java中線程的生命周期是什么

其中,BLOCKED(阻塞狀態)、WAITING(無時限等待)、TIMED_WAITING(有時限等待)可以理解為線程導致休眠狀態的三種原因。那具體是哪些情形會導致線程從  RUNNABLE 狀態轉換到這三種狀態呢?而這三種狀態又是何時轉換回 RUNNABLE 的呢?以及 NEW、TERMINATED 和 RUNNABLE  狀態是如何轉換的?

1. RUNNABLE 與 BLOCKED 的狀態轉換

只有一種場景會觸發這種轉換,就是線程等待 synchronized 的隱式鎖。synchronized  修飾的方法、代碼塊同一時刻只允許一個線程執行,其他線程只能等待,這種情況下,等待的線程就會從 RUNNABLE 轉換到 BLOCKED 狀態。而當等待的線程獲得  synchronized 隱式鎖時,就又會從 BLOCKED 轉換到 RUNNABLE 狀態。

如果你熟悉操作系統線程的生命周期的話,可能會有個疑問:線程調用阻塞式 API 時,是否會轉換到 BLOCKED  狀態呢?在操作系統層面,線程是會轉換到休眠狀態的,但是在 JVM 層面,Java 線程的狀態不會發生變化,也就是說 Java 線程的狀態會依然保持  RUNNABLE 狀態。JVM 層面并不關心操作系統調度相關的狀態,因為在 JVM 看來,等待 CPU 使用權(操作系統層面此時處于可執行狀態)與等待  I/O(操作系統層面此時處于休眠狀態)沒有區別,都是在等待某個資源,所以都歸入了 RUNNABLE 狀態。

而我們平時所謂的 Java 在調用阻塞式 API 時,線程會阻塞,指的是操作系統線程的狀態,并不是 Java 線程的狀態。

2. RUNNABLE 與 WAITING 的狀態轉換

總體來說,有三種場景會觸發這種轉換。

第一種場景,獲得 synchronized 隱式鎖的線程,調用無參數的 Object.wait() 方法。

第二種場景,調用無參數的 Thread.join() 方法。其中的 join() 是一種線程同步方法,例如有一個線程對象 thread A,當調用  A.join() 的時候,執行這條語句的線程會等待 thread A 執行完,而等待中的這個線程,其狀態會從 RUNNABLE 轉換到 WAITING。當線程  thread A 執行完,原來等待它的線程又會從 WAITING 狀態轉換到 RUNNABLE。

第三種場景,調用 LockSupport.park() 方法。其中的 LockSupport 對象,也許你有點陌生,其實 Java  并發包中的鎖,都是基于它實現的。調用 LockSupport.park() 方法,當前線程會阻塞,線程的狀態會從 RUNNABLE 轉換到 WAITING。調用  LockSupport.unpark(Thread thread) 可喚醒目標線程,目標線程的狀態又會從 WAITING 狀態轉換到 RUNNABLE。

3. RUNNABLE 與 TIMED_WAITING 的狀態轉換

有五種場景會觸發這種轉換:

  1. 調用帶超時參數的 Thread.sleep(long millis) 方法;

  2. 獲得 synchronized 隱式鎖的線程,調用帶超時參數的 Object.wait(long timeout) 方法;

  3. 調用帶超時參數的 Thread.join(long millis) 方法;

  4. 調用帶超時參數的 LockSupport.parkNanos(Object blocker, long deadline) 方法;

  5. 調用帶超時參數的 LockSupport.parkUntil(long deadline) 方法。

  6. 這里你會發現 TIMED_WAITING 和 WAITING 狀態的區別,僅僅是觸發條件多了超時參數。

4. 從 NEW 到 RUNNABLE 狀態

Java 剛創建出來的 Thread 對象就是 NEW 狀態,而創建 Thread 對象主要有兩種方法。一種是繼承 Thread 對象,重寫 run()  方法。示例代碼如下:

public class MyThread extends Thread {     @Override     public void run() {         // 線程需要執行的代碼         System.out.println(Thread.currentThread().getName());     }      public static void main(String[] args) {         // 創建線程對象         MyThread myThread = new MyThread();     } }

另一種是實現 Runnable 接口,重寫 run() 方法,并將該實現類作為創建 Thread 對象的參數。示例代碼如下:

public class Runner implements Runnable {     @Override     public void run() {         // 線程需要執行的代碼         System.out.println(Thread.currentThread().getName());     }      public static void main(String[] args) {         // 創建線程對象         Thread thread = new Thread(new Runner());     } }

NEW 狀態的線程,不會被操作系統調度,因此不會執行。Java 線程要執行,就必須轉換到 RUNNABLE 狀態。從 NEW 狀態轉換到 RUNNABLE  狀態很簡單,只要調用線程對象的 start() 方法就可以了,示例代碼如下:

public class Runner implements Runnable {     @Override     public void run() {         // 線程需要執行的代碼         System.out.println(Thread.currentThread().getName());     }      public static void main(String[] args) {         // 創建線程對象         Thread thread = new Thread(new Runner());         // 從 NEW 狀態轉換到 RUNNABLE 狀態         thread.start();     } }

5. 從 RUNNABLE 到 TERMINATED 狀態

線程執行完 run() 方法后,會自動轉換到 TERMINATED 狀態,當然如果執行 run()  方法的時候異常拋出,也會導致線程終止。有時候我們需要強制中斷 run() 方法的執行,例如 run()  方法訪問一個很慢的網絡,我們等不下去了,想終止怎么辦呢?Java 的 Thread 類里面倒是有個 stop() 方法,不過已經標記為  @Deprecated,所以不建議使用了。正確的姿勢其實是調用 interrupt() 方法。

java.lang.Thread#stop() 源碼:

@Deprecated public final void stop() {     SecurityManager security = System.getSecurityManager();     if (security != null) {         checkAccess();         if (this != Thread.currentThread()) {             security.checkPermission(SecurityConstants.STOP_THREAD_PERMISSION);         }     }     if (threadStatus != 0) {         resume();     }     stop0(new ThreadDeath()); }

那 stop() 和 interrupt() 方法的主要區別是什么呢?

stop() 方法會真的殺死線程,如果線程持有 ReentrantLock 鎖,被 stop() 的線程并不會自動調用 ReentrantLock 的  unlock() 去釋放鎖,那其他線程就再也沒機會獲得 ReentrantLock 鎖,這實在是太危險了。所以該方法就不建議使用了,類似的方法還有  suspend() 和 resume() 方法,這兩個方法同樣也都不建議使用。

而 interrupt() 方法僅僅是通知線程,線程有機會執行一些后續操作,同時也可以無視這個通知。被 interrupt  的線程,是怎么收到通知的呢?一種是異常,另一種是主動檢測。

當線程 A 處于 WAITING、TIMED_WAITING 狀態時,如果其他線程調用線程 A 的 interrupt() 方法,會使線程 A 返回到  RUNNABLE 狀態,同時線程 A 的代碼會觸發 InterruptedException 異常。上面我們提到轉換到  WAITING、TIMED_WAITING 狀態的觸發條件,都是調用了類似 wait()、join()、sleep()  這樣的方法,我們看這些方法的簽名,發現都會 throws InterruptedException 這個異常。這個異常的觸發條件就是:其他線程調用了該線程的  interrupt() 方法。

當線程 A 處于 RUNNABLE 狀態時,并且阻塞在 java.nio.channels.InterruptibleChannel  上時,如果其他線程調用線程 A 的 interrupt() 方法,線程 A 會觸發  java.nio.channels.ClosedByInterruptException 這個異常;而阻塞在  java.nio.channels.Selector 上時,如果其他線程調用線程 A 的 interrupt() 方法,線程 A 的  java.nio.channels.Selector 會立即返回。

上面這兩種情況屬于被中斷的線程通過異常的方式獲得了通知。還有一種是主動檢測,如果線程處于 RUNNABLE 狀態,并且沒有阻塞在某個 I/O  操作上,例如中斷線程 A,這時就得依賴線程 A 主動檢測中斷狀態了。如果其他線程調用線程 A 的 interrupt() 方法,那么線程 A 可以通過  isInterrupted() 方法,檢測是不是自己被中斷了。

關于“ Java中線程的生命周期是什么”的內容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業相關的知識,可以關注億速云行業資訊頻道,小編每天都會為大家更新不同的知識點。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女