Thread之一:線程生命周期及六種狀態


 《Thread之一:線程生命周期及五種狀態

Thread之二:sleep、wait、yield、join

 《juc線程池原理(四): 線程池狀態介紹

一、線程的生命周期及五種基本狀態

關於Java中線程的生命周期,首先看一下下面這張較為經典的圖:

 

 

 

上圖中基本上囊括了Java中多線程各重要知識點。掌握了上圖中的各知識點,Java中的多線程也就基本上掌握了。主要包括:

Java線程具有七種基本狀態

新建狀態(New):至今尚未啟動的線程的狀態。線程剛被創建,但尚未啟動如:Thread t = new MyThread();

就緒狀態(Runnable):當調用線程對象的start()方法(t.start();),線程即進入就緒狀態。處於就緒狀態的線程,只是說明此線程已經做好了准備,隨時等待CPU調度執行,並不是說執行了t.start()此線程立即就會執行;

運行狀態(Running)當CPU開始調度處於就緒狀態的線程時,此時線程才得以真正執行,即進入到運行狀態。注:就     緒狀態是進入到運行狀態的唯一入口,也就是說,線程要想進入運行狀態執行,首先必須處於就緒狀態中;

無限期等待(Waiting):位於對象等待池中的阻塞狀態(Blocked in object’s wait pool):當線程處於運行狀態時,如果執行了某個對象的wait()方法,Java虛擬機就會把線程放到這個對象的等待池中,這涉及到“線程通信”的內容。處於這種狀態的線程不會被分配處理器執行時間,它們要等待被其他線程顯示喚醒。以下方法會讓線程陷入無限期的等待狀態

  • 沒有設置timeout參數的Object::wait()方法
  • 沒有設置timeout參數的Thread::join()方法
  • LockSupport::park()方法

限期等待(Timed Waiting)處於這種狀態的線程也不會被分配處理器執行時間,不過無須等待其他線程顯示喚醒,在一定時間后它們由系統自動喚醒。以下方法會讓線程進入期限等待狀態:

  • Thread::sleep()方法
  • 設置了timeout參數的Object::wait()方法
  • 設置了timeout參數的Thread::join()方法
  • LockSupport::parkNanos()方法
  • LockSupport::parkUntil()方法

阻塞狀態(Blocked):處於運行狀態中的線程由於某種(當線程處於運行狀態時,試圖獲得某個對象的同步鎖時,如果該對象的同步鎖已經被其他線程占用,Java虛擬機就會把這個線程放到這個對象的鎖池中,這涉及到“線程同步”的內容。【線程在獲取synchronized同步鎖失敗(因為鎖被其它線程所占用)】)原因,暫時放棄對CPU的使用權,停止執行,此時進入阻塞狀態,直到其進入到就緒狀態,才有機會再次被CPU調用以進入到運行狀態。 

 “阻塞狀態”與“等待狀態”的區別:“阻塞狀態”在等待着獲取一個排它鎖,這個事件將在另一個線程放棄這個鎖的時候發生;而“等待狀態”則是在等待一段時間,或者喚醒動作發生。在程序進入同步區域的時候,線程就會進入阻塞狀態。

死亡狀態(Dead):線程執行完了或者因異常退出了run()方法,該線程結束生命周期。

 

JVM線程運行狀態 (JVM Thread Status)

在 java.lang.Thread.State 中定義了線程的狀態:

NEW

至今尚未啟動的線程的狀態。線程剛被創建,但尚未啟動。

RUNNABLE

可運行線程的線程狀態。線程正在JVM中執行,有可能在等待操作系統中的其他資源,比如處理器。

BLOCKED

受阻塞並且正在等待監視器的某一線程的線程狀態。處於受阻塞狀態的某一線程正在等待監視器鎖,以便進入一個同步的塊/方法,或者在調用 Object.wait 之后再次進入同步的塊/方法。
在Thread Dump日志中通常顯示為 java.lang.Thread.State: BLOCKED (on object monitor) 。

WAITING

某一等待線程的線程狀態。線程正在無期限地等待另一個線程來執行某一個特定的操作,線程因為調用下面的方法之一而處於等待狀態:

  • 不帶超時的 Object.wait 方法,日志中顯示為 java.lang.Thread.State: WAITING (on object monitor)
  • 不帶超時的 Thread.join 方法
  • LockSupport.park 方法,日志中顯示為 java.lang.Thread.State: WAITING (parking)

TIMED_WAITING

指定了等待時間的某一等待線程的線程狀態。線程正在等待另一個線程來執行某一個特定的操作,並設定了指定等待的時間,線程因為調用下面的方法之一而處於定時等待狀態:

  • Thread.sleep 方法
  • 指定超時值的 Object.wait 方法
  • 指定超時值的 Thread.join 方法
  • LockSupport.parkNanos
  • LockSupport.parkUntil

TERMINATED

線程處於終止狀態。

根據Java Doc中的說明,在給定的時間上,一個只能處於上述的一種狀態之中,並且這些狀態都是JVM的狀態,跟操作系統中的線程狀態無關。

JAVA虛擬機啟動程序步驟:

(1) Main是啟動時候的主線程,即程序入口

(2) 在main函數結束后,虛擬機會自動啟動一個DestroyJavaVM線程,該線程會等待所有user thread 線程結束后退出(即,只剩下daemon 線程和DestroyJavaVM線程自己,整個虛擬機就退出,此時daemon線程被終止),因此,如果不希望程序退出,只要創建一個非daemon的子線程,讓線程不停的sleep即可。

線程的創建

Thread類,有一個start方法,即啟動該線程。 啟動的線程會執行該類的run方法。注意:因為啟動線程時要執行某個過程,因此,通常是需要重新實現run方法的

線程的結束

run模塊執行完成主動退出,或者被其他線程強行終止。

 

通過jstack pid >1.txt

線程狀態樣例

等待狀態樣例

 "IoWaitThread" prio=6 tid=0x0000000007334800 nid=0x2b3c waiting on condition [0x000000000893f000]
   java.lang.Thread.State: WAITING (parking)
                at sun.misc.Unsafe.park(Native Method)
                - parking to wait for  <0x00000007d5c45850> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
                at java.util.concurrent.locks.LockSupport.park(LockSupport.java:156)
                at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:1987)
                at java.util.concurrent.LinkedBlockingDeque.takeFirst(LinkedBlockingDeque.java:440)
                at java.util.concurrent.LinkedBlockingDeque.take(LinkedBlockingDeque.java:629)
                at com.nbp.theplatform.threaddump.ThreadIoWaitState$IoWaitHandler2.run(ThreadIoWaitState.java:89)
                at java.lang.Thread.run(Thread.java:662) 

上面例子中,IoWaitThread 線程保持等待狀態並從 LinkedBlockingQueue 接收消息,如果 LinkedBlockingQueue 一直沒有消息,該線程的狀態將不會改變。

阻塞狀態樣例

"BLOCKED_TEST pool-1-thread-1" prio=6 tid=0x0000000006904800 nid=0x28f4 runnable [0x000000000785f000]
   java.lang.Thread.State: RUNNABLE
                at java.io.FileOutputStream.writeBytes(Native Method)
                at java.io.FileOutputStream.write(FileOutputStream.java:282)
                at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:65)
                at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:123)
                - locked <0x0000000780a31778> (a java.io.BufferedOutputStream)
                at java.io.PrintStream.write(PrintStream.java:432)
                - locked <0x0000000780a04118> (a java.io.PrintStream)
                at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:202)
                at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:272)
                at sun.nio.cs.StreamEncoder.flushBuffer(StreamEncoder.java:85)
                - locked <0x0000000780a040c0> (a java.io.OutputStreamWriter)
                at java.io.OutputStreamWriter.flushBuffer(OutputStreamWriter.java:168)
                at java.io.PrintStream.newLine(PrintStream.java:496)
                - locked <0x0000000780a04118> (a java.io.PrintStream)
                at java.io.PrintStream.println(PrintStream.java:687)
                - locked <0x0000000780a04118> (a java.io.PrintStream)
                at com.nbp.theplatform.threaddump.ThreadBlockedState.monitorLock(ThreadBlockedState.java:44)
                - locked <0x0000000780a000b0> (a com.nbp.theplatform.threaddump.ThreadBlockedState)
                at com.nbp.theplatform.threaddump.ThreadBlockedState$1.run(ThreadBlockedState.java:7)
                at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
                at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
                at java.lang.Thread.run(Thread.java:662)
   Locked ownable synchronizers:
                - <0x0000000780a31758> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
"BLOCKED_TEST pool-1-thread-2" prio=6 tid=0x0000000007673800 nid=0x260c waiting for monitor entry [0x0000000008abf000]
   java.lang.Thread.State: BLOCKED (on object monitor)
                at com.nbp.theplatform.threaddump.ThreadBlockedState.monitorLock(ThreadBlockedState.java:43)
                - waiting to lock <0x0000000780a000b0> (a com.nbp.theplatform.threaddump.ThreadBlockedState)
                at com.nbp.theplatform.threaddump.ThreadBlockedState$2.run(ThreadBlockedState.java:26)
                at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
                at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
                at java.lang.Thread.run(Thread.java:662)
   Locked ownable synchronizers:
                - <0x0000000780b0c6a0> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
"BLOCKED_TEST pool-1-thread-3" prio=6 tid=0x00000000074f5800 nid=0x1994 waiting for monitor entry [0x0000000008bbf000]
   java.lang.Thread.State: BLOCKED (on object monitor)
                at com.nbp.theplatform.threaddump.ThreadBlockedState.monitorLock(ThreadBlockedState.java:42)
                - waiting to lock <0x0000000780a000b0> (a com.nbp.theplatform.threaddump.ThreadBlockedState)
                at com.nbp.theplatform.threaddump.ThreadBlockedState$3.run(ThreadBlockedState.java:34)
                at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886
                at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
                at java.lang.Thread.run(Thread.java:662)
   Locked ownable synchronizers:
                - <0x0000000780b0e1b8> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)

在上面的例子中,BLOCKED_TEST pool-1-thread-1 線程占用了 <0x0000000780a000b0> 鎖,然而 BLOCKED_TEST pool-1-thread-2 和 BLOCKED_TEST pool-1-thread-3 threads 正在等待獲取鎖。

死鎖狀態樣例

"DEADLOCK_TEST-1" daemon prio=6 tid=0x000000000690f800 nid=0x1820 waiting for monitor entry [0x000000000805f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
                at com.nbp.theplatform.threaddump.ThreadDeadLockState$DeadlockThread.goMonitorDeadlock(ThreadDeadLockState.java:197)
                - waiting to lock <0x00000007d58f5e60> (a com.nbp.theplatform.threaddump.ThreadDeadLockState$Monitor)
                at com.nbp.theplatform.threaddump.ThreadDeadLockState$DeadlockThread.monitorOurLock(ThreadDeadLockState.java:182)
                - locked <0x00000007d58f5e48> (a com.nbp.theplatform.threaddump.ThreadDeadLockState$Monitor)
                at com.nbp.theplatform.threaddump.ThreadDeadLockState$DeadlockThread.run(ThreadDeadLockState.java:135)

   Locked ownable synchronizers:
                - None

"DEADLOCK_TEST-2" daemon prio=6 tid=0x0000000006858800 nid=0x17b8 waiting for monitor entry [0x000000000815f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
                at com.nbp.theplatform.threaddump.ThreadDeadLockState$DeadlockThread.goMonitorDeadlock(ThreadDeadLockState.java:197)
                - waiting to lock <0x00000007d58f5e78> (a com.nbp.theplatform.threaddump.ThreadDeadLockState$Monitor)
                at com.nbp.theplatform.threaddump.ThreadDeadLockState$DeadlockThread.monitorOurLock(ThreadDeadLockState.java:182)
                - locked <0x00000007d58f5e60> (a com.nbp.theplatform.threaddump.ThreadDeadLockState$Monitor)
                at com.nbp.theplatform.threaddump.ThreadDeadLockState$DeadlockThread.run(ThreadDeadLockState.java:135)

   Locked ownable synchronizers:
                - None

"DEADLOCK_TEST-3" daemon prio=6 tid=0x0000000006859000 nid=0x25dc waiting for monitor entry [0x000000000825f000]
   java.lang.Thread.State: BLOCKED (on object monitor)
                at com.nbp.theplatform.threaddump.ThreadDeadLockState$DeadlockThread.goMonitorDeadlock(ThreadDeadLockState.java:197)
                - waiting to lock <0x00000007d58f5e48> (a com.nbp.theplatform.threaddump.ThreadDeadLockState$Monitor)
                at com.nbp.theplatform.threaddump.ThreadDeadLockState$DeadlockThread.monitorOurLock(ThreadDeadLockState.java:182)
                - locked <0x00000007d58f5e78> (a com.nbp.theplatform.threaddump.ThreadDeadLockState$Monitor)
                at com.nbp.theplatform.threaddump.ThreadDeadLockState$DeadlockThread.run(ThreadDeadLockState.java:135)

   Locked ownable synchronizers:
                - None

上面的例子中,當線程 A 需要獲取線程 B 的鎖來繼續它的任務,然而線程 B 也需要獲取線程 A 的鎖來繼續它的任務的時候發生的。在 thread dump 中,你能看到 DEADLOCK_TEST-1 線程持有 0x00000007d58f5e48 鎖,並且嘗試獲取 0x00000007d58f5e60 鎖。你也能看到 DEADLOCK_TEST-2 線程持有 0x00000007d58f5e60,並且嘗試獲取 0x00000007d58f5e78,同時 DEADLOCK_TEST-3 線程持有 0x00000007d58f5e78,並且在嘗試獲取 0x00000007d58f5e48 鎖,如你所見,每個線程都在等待獲取另外一個線程的鎖,這狀態將不會被改變直到一個線程丟棄了它的鎖。

無限等待的Runnable狀態樣例

"socketReadThread" prio=6 tid=0x0000000006a0d800 nid=0x1b40 runnable [0x00000000089ef000]
   java.lang.Thread.State: RUNNABLE
                at java.net.SocketInputStream.socketRead0(Native Method)
                at java.net.SocketInputStream.read(SocketInputStream.java:129)
                at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:264)
                at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:306)
                at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:158)
                - locked <0x00000007d78a2230> (a java.io.InputStreamReader)
                at sun.nio.cs.StreamDecoder.read0(StreamDecoder.java:107)
                - locked <0x00000007d78a2230> (a java.io.InputStreamReader)
                at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:93)
                at java.io.InputStreamReader.read(InputStreamReader.java:151)
                at com.nbp.theplatform.threaddump.ThreadSocketReadState$1.run(ThreadSocketReadState.java:27)
                at java.lang.Thread.run(Thread.java:662)

上例中線程的狀態是RUNNABLE,但在下面的堆棧日志中發現socketReadThread 線程正在無限等待讀取 socket,因此不能單純通過線程的狀態來確定線程是否處於阻塞狀態,應該根據詳細的堆棧信息進行分析。

 


免責聲明!

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



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