說到線程,必定要談到線程狀態,不一樣的狀態說明線程正處於不一樣的工做機制下,不一樣的工做機制下某些動做可能對線程產生不一樣的影響。html
Java語言定義了6中狀態,而同一時刻,線程有且僅有其中的一種狀態。要獲取Java線程的狀態可使用 java.lang.Thread類中定義的 getState()方法,獲取當前線程的狀態就可使用Thread.currentThread().getState()來獲取。該方法返回的類型是一個枚舉類型,是Thread內部的一個枚舉,全稱爲「java.lang.Thread.State」,這個枚舉中定義的類型列表就是Java語言這個級別對應的線程狀態列表,包含了NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED這些值。java
如下是本文的目錄大綱:算法
1、Java的幾種線程狀態說明apache
2、Java線程狀態轉換圖tomcat
3、「VisualVM線程監控線程狀態」與「Java線程狀態」對應關係網絡
如有不正之處請多多諒解,歡迎批評指正、互相討論。app
請尊重做者勞動成果,轉載請標明原文連接:dom
http://www.cnblogs.com/trust-freedom/p/6606594.htmlsocket
一、NEW(新建)ui
java.lang.Thread.State枚舉中的NEW狀態描述:
/** * Thread state for a thread which has not yet started. */ NEW
建立後還沒有啓動的線程處於這個狀態。
意思是這個線程沒有被start()啓動,或者說還根本不是一個真正意義上的線程,從本質上講這只是建立了一個Java外殼,尚未真正的線程來運行。
不表明調用了start(),狀態就當即改變,中間還有一些步驟,若是在這個啓動的過程當中有另外一個線程來獲取它的狀態,實際上是不肯定的,要看那些中間步驟是否已經完成了。
二、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狀態。
三、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()方法
四、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(),讓主線程等待子線程執行完畢再執行
離開「等待」狀態是由於其它線程發生了喚醒動做或者到達了等待時間
五、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()方法
六、TERMINATED(結束)
/** * Thread state for a terminated thread. * The thread has completed execution. */ TERMINATED
已終止線程的線程狀態,線程已經結束執行。換句話說,run()方法走完了,線程就處於這種狀態。其實這只是Java語言級別的一種狀態,在操做系統內部可能已經註銷了相應的線程,或者將它複用給其餘須要使用線程的請求,而在Java語言級別只是經過Java代碼看到的線程狀態而已。
上圖爲我的瞭解的線程狀態轉換的各類狀況,若有不到位的地方歡迎交流指教。
經過VisualVM監控JVM時,能夠經過「線程」標籤頁查看JVM的線程信息,VisualVM的線程狀態以下:
經過dump thread stack,並與VisualVM監控信息中的線程名稱對應,找到的VisualVM每種線程狀態的線程堆棧以下:(請關注重點信息)
一、運行
"http-bio-8080-Acceptor-0" daemon prio=6 tid=0x000000000d7b4800 nid=0xa264 runnable [0x000000001197e000] Locked ownable synchronizers: |
二、休眠
"Druid-ConnectionPool-Destory-293325558" daemon prio=6 tid=0x000000000d7ad000 nid=0x9c94 waiting on condition [0x000000000bf0f000] Locked ownable synchronizers: |
三、等待
"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: |
四、駐留
"http-bio-8080-exec-2" daemon prio=6 tid=0x000000000d7b8000 nid=0x9264 waiting on condition [0x000000000ee4e000] Locked ownable synchronizers:
Locked ownable synchronizers: |
五、監視
"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虛擬機》