Java語言定義的線程狀態分析


    說到線程,一定要談到線程狀態,不同的狀態說明線程正處於不同的工作機制下,不同的工作機制下某些動作可能對線程產生不同的影響。

    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]
      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 基礎介紹


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM