原文地址: Java 併發編程 ② - 線程生命週期與狀態流轉轉載請註明出處!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
狀態,而後請求監視器鎖資源。測試
再來看,操做系統層面,線程存在五類狀態,狀態的流轉關係能夠參考下面的這張圖。spa
能夠看到,Java 中所說的線程狀態和操做系統層面的線程狀態是不太同樣的。操作系統
RUNNABLE
其實包含了OS中的RUNNING
和READY
WAITING
、TIMED_WAITING
、BLOCKED
實際上是對OS中WAITING
狀態的一個更細緻的劃分在Thread.State
源碼中也寫了這麼一句話:線程
These states are virtual machine states which do not reflect any operating system thread states.這些狀態只是線程在虛擬機中的狀態,並不反映操做系統的線程狀態。code
對於這兩個層面對比,你須要知道的是,Java的線程狀態是服務於虛擬機的。從這個角度來考慮的話,把底層OS中的RUNNING
和READY
狀態映射上來也沒多大意義,所以,統一成爲RUNNABLE
狀態是不錯的選擇,而對WAITING
狀態更細緻的劃分,也是出於這麼一個考慮。orm
圖很詳細,結合前面的內容一塊兒食用。
關於阻塞狀態,這裏還要多說幾句話,咱們上面說的,都是在JVM 代碼層面的實際線程狀態。可是在一些書好比《碼出高效》中,會把Java 線程的阻塞狀態分爲:
這裏演示一下,如何在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 的用法和原理,感興趣請持續關注。
若是本文有幫助到你,但願能點個贊,這是對個人最大動力🤝🤝🤗🤗。