標籤:spec jmx osi visualvm ica com 取資料 請求 問題
說到線程,一定要談到線程狀態,不同的狀態說明線程正處於不同的工作機制下,不同的工作機制下某些動作可能對線程產生不同的影響。
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線程狀態”對應關係
一、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]
java.lang.Thread.State: RUNNABLE
at java.net.DualStackPlainSocketImpl.accept0(Native Method)
at java.net.DualStackPlainSocketImpl.socketAccept(DualStackPlainSocketImpl.java:131)
at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:398)
at java.net.PlainSocketImpl.accept(PlainSocketImpl.java:199)
- locked <0x00000000c2303850> (a java.net.SocksSocketImpl)
at java.net.ServerSocket.implAccept(ServerSocket.java:530)
at java.net.ServerSocket.accept(ServerSocket.java:498)
at org.apache.tomcat.util.net.DefaultServerSocketFactory.acceptSocket(DefaultServerSocketFactory.java:60)
at org.apache.tomcat.util.net.JIoEndpoint$Acceptor.run(JIoEndpoint.java:220)
at java.lang.Thread.run(Thread.java:745)
Locked ownable synchronizers:
- None |
2、休眠
|
"Druid-ConnectionPool-Destory-293325558" daemon prio=6 tid=0x000000000d7ad000 nid=0x9c94 waiting on condition [0x000000000bf0f000]
java.lang.Thread.State: TIMED_WAITING (sleeping) at java.lang.Thread.sleep(Native Method) at com.alibaba.druid.pool.DruidDataSource$DestroyConnectionThread.run(DruidDataSource.java:1685)
Locked ownable synchronizers:
- None |
3、等待
|
"Finalizer" daemon prio=8 tid=0x0000000009349000 nid=0xa470 in Object.wait() [0x000000000a82f000]
java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method)
- waiting on <0x00000000c22a0108> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
- locked <0x00000000c22a0108> (a java.lang.ref.ReferenceQueue.Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)
Locked ownable synchronizers:
- None
"JMX server connection timeout 45" daemon prio=6 tid=0x000000000e846000 nid=0xab10 in Object.wait() [0x00000000137df000]
java.lang.Thread.State: TIMED_WAITING (on object monitor) at java.lang.Object.wait(Native Method)
- waiting on <0x00000000c55da3f0> (a [I)
at com.sun.jmx.remote.internal.ServerCommunicatorAdmin$Timeout.run(ServerCommunicatorAdmin.java:168)
- locked <0x00000000c55da3f0> (a [I)
at java.lang.Thread.run(Thread.java:745)
Locked ownable synchronizers:
- None |
4、駐留
|
"http-bio-8080-exec-2" daemon prio=6 tid=0x000000000d7b8000 nid=0x9264 waiting on condition [0x000000000ee4e000]
java.lang.Thread.State: WAITING (parking) at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000c5629bc8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject.await(AbstractQueuedSynchronizer.java:2043)
at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:104)
at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:32)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1068)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
at java.util.concurrent.ThreadPoolExecutor.Worker.run(ThreadPoolExecutor.java:615)
at org.apache.tomcat.util.threads.TaskThread.WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
Locked ownable synchronizers:
- None
"pool-9-thread-1" prio=6 tid=0x000000000d7b2000 nid=0xd5fc waiting on condition [0x000000001187e000]
java.lang.Thread.State: TIMED_WAITING (parking) at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000c563b9e0> (a java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject)
at java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:226)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2082)
at java.util.concurrent.ScheduledThreadPoolExecutor.DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1090)
at java.util.concurrent.ScheduledThreadPoolExecutor.DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:807)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1068)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
at java.util.concurrent.ThreadPoolExecutor.Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:745)
Locked ownable synchronizers:
- None |
5、監視
|
"Thread-1" prio=6 tid=0x000000000a8a1800 nid=0xfdb4 waiting for monitor entry [0x000000000b4de000]
java.lang.Thread.State: BLOCKED (on object monitor) at com.jy.modules.test.Test2$T.run(Test2.java:58)
- waiting to lock <0x00000000eab757e0> (a java.lang.Object)
Locked ownable synchronizers:
- None |
“VisualVM線程監控線程狀態”與“Java線程狀態”對應關係總結:
可以看出,VisualVM的線程狀態將“WAITING”和“TIMED_WAITING”兩個狀態根據行程狀態的原因做了細化(其實java的thread stack dump上已經細化了)
如造成“TIMED_WAITING”狀態的原因可能是 :
Thread.sleep() -- 休眠
Object.wait(timeout) -- 等待
LockSupport.parkUntil(deadline) -- 駐留
參考資料:
《深入理解Java虛擬機器》
《Java特種兵》5.1 基礎介紹
Java語言定義的線程狀態分析