Java 併發編程 ② - 線程生命週期與狀態流轉

原文地址: Java 併發編程 ② - 線程生命週期與狀態流轉

轉載請註明出處!java

前言

往期文章:編程

繼上一篇結尾講的,這一篇文章主要是講線程的生命週期以及狀態流轉。主要內容有:併發

  • Java 中對線程狀態的定義,與操做系統線程狀態的對比
  • 線程狀態的流轉圖
  • 如何本身驗證狀態的流轉

1、Java 線程的狀態

先來談一談Java 中線程的狀態。在 java.lang.Thread.State 類是 Thread的內部枚舉類,在裏面定義了Java 線程的六個狀態,-註釋信息也很是的詳細。post

public enum State {
        
        /**
         * Thread state for a thread which has not yet started.
         * 初始態,表明線程剛建立出來,可是尚未 start 的狀態
         */
        NEW,

        /**
         * 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.
         * 
         * 運行態,表明線程正在運行或者等待操做系統資源,如CPU資源
         */
        RUNNABLE,

        /**
         * Thread state for a thread blocked waiting for a monitor lock.
         * A thread in the blocked state is waiting for a monitor lock
         * to enter a synchronized block/method or
         * reenter a synchronized block/method after calling
         * {@link Object#wait() Object.wait}.
         * 
         * 阻塞態,表明線程正在等待一個監視器鎖(即咱們常說的synchronized)
         * 或者是在調用了Object.wait以後被notify()從新進入synchronized代碼塊
         */
        BLOCKED,

        /**
         * Thread state for a waiting thread.
         * A thread is in the waiting state due to calling one of the
         * following methods:
         * <ul>
         *   <li>{@link Object#wait() Object.wait} with no timeout</li>
         *   <li>{@link #join() Thread.join} with no timeout</li>
         *   <li>{@link LockSupport#park() LockSupport.park}</li>
         * </ul>
         *
         * <p>A thread in the waiting state is waiting for another thread to
         * perform a particular action.
         *
         * For example, a thread that has called <tt>Object.wait()</tt>
         * on an object is waiting for another thread to call
         * <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on
         * that object. A thread that has called <tt>Thread.join()</tt>
         * is waiting for a specified thread to terminate.
         * 
         * 等待態,調用如下方法會進入等待狀態:
         * 1. 調用不會超時的Object.wait()方法
         * 2. 調用不會超時的Thread.join()方法
         * 3. 調用不會超時的LockSupport.park()方法
         */
        WAITING,

        /**
         * Thread state for a waiting thread with a specified waiting time.
         * A thread is in the timed waiting state due to calling one of
         * the following methods with a specified positive waiting time:
         * <ul>
         *   <li>{@link #sleep Thread.sleep}</li>
         *   <li>{@link Object#wait(long) Object.wait} with timeout</li>
         *   <li>{@link #join(long) Thread.join} with timeout</li>
         *   <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li>
         *   <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li>
         * </ul>
         * 
         * 超時等待態,在調用瞭如下方法後會進入超時等待狀態
         * 1. Thread.sleep()方法後
         * 2. Object.wait(timeout)方法
         * 3. Thread.join(timeout)方法
         * 4. LockSupport.parkNanos(nanos)方法
         * 5. LockSupport.parkUntil(deadline)方法
         */
        TIMED_WAITING,

        /**
         * Thread state for a terminated thread.
         * The thread has completed execution.
         * 
         * 終止態,表明線程已經執行完畢
         */
        TERMINATED;
    }

關於上面JDK源碼中對於BLOCKED狀態的註釋,這裏有一點須要補充的,就是若是是線程調用了Object.wait(timeout)方法進入TIMED_WAITING狀態以後,若是是由於超過指定時間,脫離TIMED_WAITING狀態,若是接下去線程是要從新進入synchronize 代碼塊的話,也是會先進入等待隊列,變成BLOCKED狀態,而後請求監視器鎖資源。測試

1.1 操做系統中的線程狀態

再來看,操做系統層面,線程存在五類狀態,狀態的流轉關係能夠參考下面的這張圖。spa

能夠看到,Java 中所說的線程狀態和操做系統層面的線程狀態是不太同樣的。操作系統

  • Java 中的 RUNNABLE 其實包含了OS中的RUNNINGREADY
  • Java 中的WAITINGTIMED_WAITINGBLOCKED實際上是對OS中WAITING狀態的一個更細緻的劃分

Thread.State源碼中也寫了這麼一句話:線程

These states are virtual machine states which do not reflect any operating system thread states.

這些狀態只是線程在虛擬機中的狀態,並不反映操做系統的線程狀態code

對於這兩個層面對比,你須要知道的是,Java的線程狀態是服務於虛擬機的。從這個角度來考慮的話,把底層OS中的RUNNINGREADY狀態映射上來也沒多大意義,所以,統一成爲RUNNABLE 狀態是不錯的選擇,而對WAITING狀態更細緻的劃分,也是出於這麼一個考慮。orm

2、狀態流轉圖

圖很詳細,結合前面的內容一塊兒食用。

關於阻塞狀態,這裏還要多說幾句話,咱們上面說的,都是在JVM 代碼層面的實際線程狀態。可是在一些書好比《碼出高效》中,會把Java 線程的阻塞狀態分爲:

  • 同步阻塞:即鎖被其餘線程佔用
  • 主動阻塞:指調用了Thread 的某些方法,主動讓出CPU執行權,好比sleep()、join()等
  • 等待阻塞:執行了wait()系列方法

3、測試

這裏演示一下,如何在IDEA 上面來驗證上述的狀態流轉。有疑問或者有興趣的讀者能夠按照一樣的方法來驗證。

我這裏想要用代碼驗證下面的狀況,

就是若是是線程1調用了 Object.wait(timeout)方法進入 TIMED_WAITING狀態以後,若是是由於超過指定時間,脫離 TIMED_WAITING狀態,若是接下去線程是要從新進入 synchronize 代碼塊的話,也是會先進入等待隊列,變成 BLOCKED狀態,而後請求監視器鎖資源。
public class ThreadLifeTempTest {

    public static void main(String[] args) {
        Object object = new Object();

        new Thread(()->{
            synchronized (object) {
                try {
                    System.out.println("thread1 waiting");
                    // 等待10s,進入Timed_Waiting
                    // 10s 後會進入Blocked,獲取object的監視器鎖
                    object.wait(10000);
                    System.out.println("thread1 after waiting");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "Thread1").start();

        new Thread(()->{
            synchronized (object) {
                try {
                    // sleep也不會釋放鎖,因此thread1 不會獲取到鎖
                    Thread.sleep(10000000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "Thread2").start();
    }
}

使用IDEA的RUN模式運行代碼,而後點擊左邊的一個攝像頭按鈕(dump thread),查看各線程的狀態。

在Thread 1 等待 10s中時,dump的結果:Thread 1和Thread 2都處於 TIMED_WAITING狀態,

"Thread2" #13 prio=5 os_prio=0 tid=0x0000000020196800 nid=0x65b8 waiting on condition [0x0000000020afe000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
    at java.lang.Thread.sleep(Native Method)
    at main.java.concurrent.thread.ThreadLifeTempTest.lambda$main$1(ThreadLifeTempTest.java:33)
    - locked <0x000000076b71c748> (a java.lang.Object)
    at main.java.concurrent.thread.ThreadLifeTempTest$$Lambda$2/1096979270.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)

"Thread1" #12 prio=5 os_prio=0 tid=0x0000000020190800 nid=0x25fc in Object.wait() [0x00000000209ff000]
   java.lang.Thread.State: TIMED_WAITING (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x000000076b71c748> (a java.lang.Object)
    at main.java.concurrent.thread.ThreadLifeTempTest.lambda$main$0(ThreadLifeTempTest.java:21)
    - locked <0x000000076b71c748> (a java.lang.Object)
    at main.java.concurrent.thread.ThreadLifeTempTest$$Lambda$1/1324119927.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)

在Thread 1 等待 10s以後,Thread 1從新進入synchronize 代碼塊,進入等待隊列,變成BLOCKED狀態

"Thread2" #13 prio=5 os_prio=0 tid=0x0000000020196800 nid=0x65b8 waiting on condition [0x0000000020afe000]
   java.lang.Thread.State: TIMED_WAITING (sleeping)
    at java.lang.Thread.sleep(Native Method)
    at main.java.concurrent.thread.ThreadLifeTempTest.lambda$main$1(ThreadLifeTempTest.java:33)
    - locked <0x000000076b71c748> (a java.lang.Object)
    at main.java.concurrent.thread.ThreadLifeTempTest$$Lambda$2/1096979270.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)

"Thread1" #12 prio=5 os_prio=0 tid=0x0000000020190800 nid=0x25fc waiting for monitor entry [0x00000000209ff000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at java.lang.Object.wait(Native Method)
    - waiting on <0x000000076b71c748> (a java.lang.Object)
    at main.java.concurrent.thread.ThreadLifeTempTest.lambda$main$0(ThreadLifeTempTest.java:21)
    - locked <0x000000076b71c748> (a java.lang.Object)
    at main.java.concurrent.thread.ThreadLifeTempTest$$Lambda$1/1324119927.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:748)

小結

在本篇文章中,主要講解了線程的生命週期,各個狀態以及狀態流轉。若是對線程狀態的變化還有不瞭解的,能夠藉助最後一部分的測試方法來實際驗證,幫助理解。

下一章,內容是介紹ThreadLocal 和 InheritableThreadLocal 的用法和原理,感興趣請持續關注。

若是本文有幫助到你,但願能點個贊,這是對個人最大動力🤝🤝🤗🤗。

參考

相關文章
相關標籤/搜索