說到線程,一定要談到線程狀態,不同的狀態說明線程正處於不同的工作機制下,不同的工作機制下某些動作可能對線程產生不同的影響。
Java語言定義了6中狀態,而同一時刻,線程有且僅有其中的一種狀態。要獲取Java線程的狀態可以使用 java.lang.Thread類中定義的 getState()方法,獲取當前線程的狀態就可以使用Thread.currentThread().getState()來獲取。該方法返回的類型是一個枚舉類型,是Thread內部的一個枚舉,全稱為“java.lang.Thread.State”,這個枚舉中定義的類型列表就是Java語言這個級別對應的線程狀態列表,包含了NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED這些值。
以下是本文的目錄大綱:
一、Java的幾種線程狀態說明
二、Java線程狀態轉換圖
三、“VisualVM線程監控線程狀態”與“Java線程狀態”對應關系
若有不正之處請多多諒解,歡迎批評指正、互相討論。
請尊重作者勞動成果,轉載請標明原文鏈接:
http://www.cnblogs.com/trust-freedom/p/6606594.html
一、Java的幾種線程狀態說明
1、NEW(新建)
java.lang.Thread.State枚舉中的NEW狀態描述:
/** * Thread state for a thread which has not yet started. */ NEW
創建后尚未啟動的線程處於這個狀態。
意思是這個線程沒有被start()啟動,或者說還根本不是一個真正意義上的線程,從本質上講這只是創建了一個Java外殼,還沒有真正的線程來運行。
不代表調用了start(),狀態就立即改變,中間還有一些步驟,如果在這個啟動的過程中有另一個線程來獲取它的狀態,其實是不確定的,要看那些中間步驟是否已經完成了。
2、RUNNABLE(可運行)
java.lang.Thread.State枚舉中的RUNNABLE狀態描述:
/** * 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
RUNNABLE狀態包括了操作系統線程狀態中的Running和Ready,也就是處於此狀態的線程可能正在運行,也可能正在等待系統資源,如等待CPU為它分配時間片,如等待網絡IO讀取數據。
RUNNABLE狀態也可以理解為存活着正在嘗試征用CPU的線程(有可能這個瞬間並沒有占用CPU,但是它可能正在發送指令等待系統調度)。由於在真正的系統中,並不是開啟一個線程后,CPU就只為這一個線程服務,它必須使用許多調度算法來達到某種平衡,不過這個時候線程依然處於RUNNABLE狀態。
3、BLOCKED(阻塞)
java.lang.Thread.State枚舉中的BLOCKED狀態描述:
/** * 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語句塊/方法時,如果沒有獲取到鎖,會變成BLOCKED * 或者在調用Object.wait()后,被notify()喚醒,再次進入synchronized語句塊/方法時,如果沒有獲取到鎖,會變成BLOCKED */ BLOCKED
BLOCKED稱為阻塞狀態,或者說線程已經被掛起,它“睡着”了,原因通常是它在等待一個“鎖”,當嘗試進入一個synchronized語句塊/方法時,鎖已經被其它線程占有,就會被阻塞,直到另一個線程走完臨界區或發生了相應鎖對象的wait()操作后,它才有機會去爭奪進入臨界區的權利
在Java代碼中,需要考慮synchronized的粒度問題,否則一個線程長時間占用鎖,其它爭搶鎖的線程會一直阻塞,直到擁有鎖的線程釋放鎖
處於BLOCKED狀態的線程,即使對其調用 thread.interrupt()也無法改變其阻塞狀態,因為interrupt()方法只是設置線程的中斷狀態,即做一個標記,不能喚醒處於阻塞狀態的線程
注意:ReentrantLock.lock()操作后進入的是WAITING狀態,其內部調用的是LockSupport.park()方法
4、WAITING(無限期等待)
/** * 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. */ WAITING
處於這種狀態的線程不會被分配CPU執行時間,它們要等待顯示的被其它線程喚醒。這種狀態通常是指一個線程擁有對象鎖后進入到相應的代碼區域后,調用相應的“鎖對象”的wait()方法操作后產生的一種結果。變相的實現還有LockSupport.park()、Thread.join()等,它們也是在等待另一個事件的發生,也就是描述了等待的意思。
以下方法會讓線程陷入無限期等待狀態:
(1)沒有設置timeout參數的Object.wait()
(2)沒有設置timeout參數的Thread.join()
(3)LockSupport.park()
注意:
LockSupport.park(Object blocker) 會掛起當前線程,參數blocker是用於設置當前線程的“volatile Object parkBlocker 成員變量”
parkBlocker 是用於記錄線程是被誰阻塞的,可以通過LockSupport.getBlocker()獲取到阻塞的對象,用於監控和分析線程用的。
“阻塞”與“等待”的區別:
(1)“阻塞”狀態是等待着獲取到一個排他鎖,進入“阻塞”狀態都是被動的,離開“阻塞”狀態是因為其它線程釋放了鎖,不阻塞了;
(2)“等待”狀態是在等待一段時間 或者 喚醒動作的發生,進入“等待”狀態是主動的
如主動調用Object.wait(),如無法獲取到ReentraantLock,主動調用LockSupport.park(),如主線程主動調用 subThread.join(),讓主線程等待子線程執行完畢再執行
離開“等待”狀態是因為其它線程發生了喚醒動作或者到達了等待時間
5、TIMED_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> */ TIMED_WAITING
處於這種狀態的線程也不會被分配CPU執行時間,不過無需等待被其它線程顯示的喚醒,在一定時間之后它們會由系統自動的喚醒。
以下方法會讓線程進入TIMED_WAITING限期等待狀態:
(1)Thread.sleep()方法
(2)設置了timeout參數的Object.wait()方法
(3)設置了timeout參數的Thread.join()方法
(4)LockSupport.parkNanos()方法
(5)LockSupport.parkUntil()方法
6、TERMINATED(結束)
/** * Thread state for a terminated thread. * The thread has completed execution. */ TERMINATED
已終止線程的線程狀態,線程已經結束執行。換句話說,run()方法走完了,線程就處於這種狀態。其實這只是Java語言級別的一種狀態,在操作系統內部可能已經注銷了相應的線程,或者將它復用給其他需要使用線程的請求,而在Java語言級別只是通過Java代碼看到的線程狀態而已。
二、Java線程狀態轉換圖
上圖為個人了解的線程狀態轉換的各種情況,如有不到位的地方歡迎交流指教。
三、“VisualVM線程監控線程狀態”與“Java線程狀態”對應關系
通過VisualVM監控JVM時,可以通過“線程”標簽頁查看JVM的線程信息,VisualVM的線程狀態如下:
通過dump thread stack,並與VisualVM監控信息中的線程名稱對應,找到的VisualVM每種線程狀態的線程堆棧如下:(請關注重點信息)
1、運行
"http-bio-8080-Acceptor-0" daemon prio=6 tid=0x000000000d7b4800 nid=0xa264 runnable [0x000000001197e000] Locked ownable synchronizers: |
2、休眠
"Druid-ConnectionPool-Destory-293325558" daemon prio=6 tid=0x000000000d7ad000 nid=0x9c94 waiting on condition [0x000000000bf0f000] Locked ownable synchronizers: |
3、等待
"Finalizer" daemon prio=8 tid=0x0000000009349000 nid=0xa470 in Object.wait() [0x000000000a82f000] - locked <0x00000000c22a0108> (a java.lang.ref.ReferenceQueue.Lock) Locked ownable synchronizers:
"JMX server connection timeout 45" daemon prio=6 tid=0x000000000e846000 nid=0xab10 in Object.wait() [0x00000000137df000] Locked ownable synchronizers: |
4、駐留
"http-bio-8080-exec-2" daemon prio=6 tid=0x000000000d7b8000 nid=0x9264 waiting on condition [0x000000000ee4e000] Locked ownable synchronizers:
Locked ownable synchronizers: |
5、監視
"Thread-1" prio=6 tid=0x000000000a8a1800 nid=0xfdb4 waiting for monitor entry [0x000000000b4de000] Locked ownable synchronizers: |
“VisualVM線程監控線程狀態”與“Java線程狀態”對應關系總結:
可以看出,VisualVM的線程狀態將“WAITING”和“TIMED_WAITING”兩個狀態根據行程狀態的原因做了細化(其實java的thread stack dump上已經細化了)
如造成“TIMED_WAITING”狀態的原因可能是 :
Thread.sleep() -- 休眠
Object.wait(timeout) -- 等待
LockSupport.parkUntil(deadline) -- 駐留
參考資料:
《深入理解Java虛擬機》