注: 該文章的原文是由 Tae Jin Gu 編寫,原文地址為 How to Analyze Java Thread Dumps
當有障礙,或者是一個基於 JAVA 的 WEB 應用運行的比預期慢的時候,我們需要使用 thread dumps
。如果對於你來說,thread dumps
是非常復雜的,這篇文章或許能對你有所幫助。在這里我將解釋在 JAVA 中什么是 threads
,他們的類型,怎么被創建的,怎樣管理它們,你怎樣從正在運行的應用中 dump threads
,最后你可以怎樣分析它以及確定瓶頸或者是阻塞線程。本文來自於 JAVA 應用程序長期調試經驗的結果。
Java and Thread
一個 web 服務器使用幾十到幾百個線程來處理大量並發用戶,如果一個或多個線程使用相同的資源,線程之間的競爭就不可避免了,並且有時候可能會發生死鎖。
Thread contention 是一個線程等待鎖的一個狀態,這個鎖被另外一個線程持有,等待被釋放,不同的線程頻繁訪問 WEB 應用的共享資源。例如,記錄一條日志,線程嘗試記錄日志之前必須先獲取鎖來訪問共享資源。
死鎖是線程競爭的一個特殊狀態,一個或是多個線程在等待其他線程完成它們的任務為了完成它們自己的任務。
線程競爭會引起各種不同的問題,為了分析這些這些問題,你需要使用 dump threads,dump threads
能給你提供每個線程的精確狀態信息。
JAVA 線程的背景資料
線程同步
一個線程可以與其他線程在同一時間內被處理。為了確保一致性,當多個線程試圖使用共享資源的時候,通過使用 hread synchronization
在同一時間內,應該只有一個線程能訪問共享資源
JAVA 中的線程同步可以使用監視器,每個 JAVA 對象都有一個單獨的監視器,這個監視器僅僅只能被一個線程擁有,對於擁有一個由不同的線程所擁有的監視器的線程,確實需要在隊列中等待,以便其他線程釋放它的監視器。
線程狀態
為了分析一個 thread dump
文件,你需要知道線程狀態。線程情況在 java.lang.Thread.State
中闡明了。
圖1:線程狀態
- NEW:線程剛被創建,但是還沒有被處理。
- RUNNABLE:線程占用了 CPU 並且處理了一個任務。(或是是在等待狀態由於操作系統的資源分配)
- BLOCKED:該線程正在等待另外的不同的線程釋放鎖,以便獲取監視器鎖
- WAITING:該線程正在等待,通過使用了 wait, join 或者是 park 方法
- TIMED_WAITING:該線程正在等待,通過使用了 sleep, wait, join 或者是 park 方法。(這個與
WAITING
不同是通過方法參數指定了最大等待時間,WAITING
可以通過時間或者是外部的變化解除)
線程類型
JAVA 的線程類型分為以下兩種:
- daemon threads;
- 非 daemon threads。
Daemon threads 將停止工作當沒有其他任何非 Daemon threads
時。即使你不創建任何線程,JAVA 應用也將默認創建幾個線程。他們大部分是 daemon threads
。主要用於任務處理比如內存回收或者是 JMX
。
一個運行 static void main(String[] args)
方法的線程被作為非 daemon threads
線程創建,並且當該線程停止工作的時候,所有任何其他 daemon threads
也將停止工作。(這個運行在 main 方法中的線程被稱為 VM thread in HotSpot VM)
獲取一個 Thread Dump
我們將介紹三種最常用的方法,記住,有非常多的其他方法可以獲取thread dump
,一個 thread dump
僅僅只能在測量的時候顯示線程狀態。因此為了看得線程狀態的變化,建議每隔5秒提取5到10次的記錄。
使用 jstack 獲取 Thread Dump
在 JDK1.6 或者是更高的版本中,通過使用 jstack, 在 MS Windows 平台上可能可以獲取到 Thread Dump
。
通過使用 jps
檢查當前正在運行的JAVA進程的 PID。
[user@linux ~]$ jps -v
25780 RemoteTestRunner -Dfile.encoding=UTF-8 25590 sub.rmi.registry.RegistryImpl 2999 -Dapplication.home=/home1/user/java/jdk.1.6.0_24 -Xms8m 26300 sun.tools.jps.Jps -mlvV -Dapplication.home=/home1/user/java/jdk.1.6.0_24 -Xms8m
使用明確的 PID 作為 jstack
的參數來獲取 thread dumps
。
[user@linux ~]$ jstack -f 5824
使用 jVisualVM 生成 Thread Dump
通過使用一個程序 jVisualVM
來生成 Thread Dump
。
如上圖在左側的任務表示當前正在運行的進程列表,點擊你想要信息的那個線程,然后選擇 thread tab
頁來檢查實時的線程信息。點擊右邊的 Thread Dump
按鈕來獲取 thread dump
文件。
在 Linux 控制台生成
通過使用 ps -ef
命令來獲取當前正在運行的 JAVA 應用程序的進程 ID。
[user@linux ~]$ ps - ef | grep java
user 2477 1 0 Dec23 ? 00:10:45 ... user 25780 25361 0 15:02 pts/3 00:00:02 ./jstatd -J -Djava.security.policy=jstatd.all.policy -p 2999 user 26335 25361 0 15:49 pts/3 00:00:00 grep java
使用精確的 pid 作為 kill –SIGQUIT(3)
的參數來獲取 thread dump
。
Thread Dump 文件的 線程信息
"pool-1-thread-13" prio=6 tid=0x000000000729a000 nid=0x2fb4 runnable [0x0000000007f0f000] 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 <0x0000000780b7e688> (a java.io.InputStreamReader) at java.io.InputStreamReader.read(InputStreamReader.java:167) at java.io.BufferedReader.fill(BufferedReader.java:136) at java.io.BufferedReader.readLine(BufferedReader.java:299) - locked <0x0000000780b7e688> (a java.io.InputStreamReader) at java.io.BufferedReader.readLine(BufferedReader.java:362)
- 線程名字:當使用
Java.lang.Thread
類生成一個線程的時候,該線程將被命名為Thread-(Number)
。但是當使用java.util.concurrent.ThreadFactory
類的時候,它將被命名為pool-(number)-thread-(number)
。 - 優先級:代表該線程的優先級
- 線程 ID:代表該線程的唯一 ID,(一些有用的信息,比如該線程的 CPU 使用率或者是內存使用率,都能通過該線程 ID 獲取到)。
- 線程狀態:代表該線程當前的狀態
- 線程調用棧:代表該線程的調用棧信息
Thread Dump Patterns by Type When Unable to Obtain a Lock (BLOCKED)
這個應用程序的整體性能下降是因為一個線程占用了鎖阻止了其他線程獲得鎖,在下面的示例中,BLOCKED_TEST pool-1-thread-1
線程占用了 <0x0000000780a000b0>
鎖,然而 BLOCKED_TEST pool-1-thread-2
和 BLOCKED_TEST pool-1-thread-3 threads
正在等待獲取鎖。
"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)
當在死鎖狀態
這是當線程 A 需要獲取線程 B 的鎖來繼續它的任務,然而線程 B 也需要獲取線程 A 的鎖來繼續它的任務的時候發生的。在thread dump
中,你能看到 DEADLOCK_TEST-1
線程持有 0x00000007d58f5e48
鎖,並且嘗試獲取 0x00000007d58f5e60
鎖。你也能看到 DEADLOCK_TEST-2
線程持有 0x00000007d58f5e60
,並且嘗試獲取 0x00000007d58f5e78
,同時 DEADLOCK_TEST-3
線程持有 0x00000007d58f5e78
,並且在嘗試獲取 0x00000007d58f5e48
鎖,如你所見,每個線程都在等待獲取另外一個線程的鎖,這狀態將不會被改變直到一個線程丟棄了它的鎖。
"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
當持續等待從遠處服務器接收消息
該線程是正常的,因為它的狀態為 RUNNABLE,盡管如此,當你按照時間順序排列 Thread Dump
,你會發現 socketReadThread
線程正在無限等待讀取 socket。
"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)
當 Waiting 時
線程保持在 Waiting
狀態,在 Thread Dump
中,IoWaitThread
線程保持等待狀態來從 LinkedBlockingQueue
接收消息。如果 LinkedBlockingQueue
一直沒有消息,該線程的狀態將不會改變。
"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)
當線程的資源不能正常的被組織
不必要的線程會堆積起來,當線程的資源不能被正常的組織的話,如果這個發送了,建議監控線程組織過程或檢查線程終止的條件。
使用 Thread Dump 怎樣解決問題
示例1:當 CPU 利用率高的異常
- 提取獲取最高 CPU 使用率的線程。
[user@linux ~]$ ps -mo pid.lwp.stime.time.cpu -C java PID LWP STIME TIME %CPU 10029 - Dec07 00:02:02 99.5 - 10039 Dec07 00:00:00 0.1 - 10040 Dec07 00:00:00 95.5
從這個應用中,發現使用 CPU 最高的線程。
獲取使用 CPU 最多的輕量級進程(LWP),把它的唯一標示碼 (10039) 轉換成十六進制 (0x2737)。
- 然后獲取進程的
Thread Dump
,檢查進程的動作。
通過 PID 10029 來提取應用程序的 Thread Dump
,然后通過一個 nid 0x2737 來找到這個線程。
"NioProcessor-2" prio=10 tid=0x0a8d2800 nid=0x2737 runnable [0x49aa5000] java.lang.Thread.State: RUNNABLE at sun.nio.ch.EPollArrayWrapper.epollWait(Native Method) at sun.nio.ch.EPollArrayWrapper.poll(EPollArrayWrapper.java:210) at sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:65) at sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:69) - locked <0x74c52678> (a sun.nio.ch.Util$1) - locked <0x74c52668> (a java.util.Collections$UnmodifiableSet) - locked <0x74c501b0> (a sun.nio.ch.EPollSelectorImpl) at sun.nio.ch.SelectorImpl.select(SelectorImpl.java:80) at external.org.apache.mina.transport.socket.nio.NioProcessor.select(NioProcessor.java:65) at external.org.apache.mina.common.AbstractPollingIoProcessor$Worker.run(AbstractPollingIoProcessor.java:708) at external.org.apache.mina.util.NamePreservingRunnable.run(NamePreservingRunnable.java:51) 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)
每個小時的幾個時間提取 Thread Dump
,然后檢查線程的狀態來確定問題。
示例2:當進程的性能異常的慢
多次獲得 thread dumps
后,找出 BLOCKED
狀態的線程列表。
" DB-Processor-13" daemon prio=5 tid=0x003edf98 nid=0xca waiting for monitor entry [0x000000000825f000] java.lang.Thread.State: BLOCKED (on object monitor) at beans.ConnectionPool.getConnection(ConnectionPool.java:102) - waiting to lock <0xe0375410> (a beans.ConnectionPool) at beans.cus.ServiceCnt.getTodayCount(ServiceCnt.java:111) at beans.cus.ServiceCnt.insertCount(ServiceCnt.java:43) "DB-Processor-14" daemon prio=5 tid=0x003edf98 nid=0xca waiting for monitor entry [0x000000000825f020] java.lang.Thread.State: BLOCKED (on object monitor) at beans.ConnectionPool.getConnection(ConnectionPool.java:102) - waiting to lock <0xe0375410> (a beans.ConnectionPool) at beans.cus.ServiceCnt.getTodayCount(ServiceCnt.java:111) at beans.cus.ServiceCnt.insertCount(ServiceCnt.java:43) " DB-Processor-3" daemon prio=5 tid=0x00928248 nid=0x8b waiting for monitor entry [0x000000000825d080] java.lang.Thread.State: RUNNABLE at oracle.jdbc.driver.OracleConnection.isClosed(OracleConnection.java:570) - waiting to lock <0xe03ba2e0> (a oracle.jdbc.driver.OracleConnection) at beans.ConnectionPool.getConnection(ConnectionPool.java:112) - locked <0xe0386580> (a java.util.Vector) - locked <0xe0375410> (a beans.ConnectionPool) at beans.cus.Cue_1700c.GetNationList(Cue_1700c.java:66) at org.apache.jsp.cue_1700c_jsp._jspService(cue_1700c_jsp.java:120)
在多次獲取 thread dumps
后,取得 BLOCKED
狀態的線程列表。
如果線程是 BLOCKED
的,提取線程嘗試獲取的相關聯的鎖。
通過 thread dumps
,你能確定線程狀態停止在 BLOCKED
,因為鎖 <0xe0375410>
不能被獲取到,這個問題可以通過分析當前夯住的線程的 stack trace
來解決。
使用 DBMS
的時候,為什么以上的范例經常出現再應用程序中,這有兩個原因。第一個原因是配置不當。盡管事實是該線程仍然在工作,它們不能展示它們最好的性能,因為 DBCP
的配置文件沒有配置正確。如果你多次提取 thread dumps
並且對比它們,你將經常看到被阻塞的線程之前處於不同的狀態。
第二個原因是不正常的連接。當與 DBMS
的連接保持在不正常的狀態,線程將等待直到超時。在這個例子中,通過多次提取 thread dumps
並對比它們,你會發現與 DBMS 相關的線程仍然在阻塞狀態。通過適當改變一些值,比如超時時間,你可以縮短問題發生的時間。
為簡單的 Thread Dump 命名線程編碼
當使用 java.lang.Thread
對象創建線程的時候,線程被命名為 Thread-(Number) 。當使用 java.util.concurrent.DefaultThreadFactory
對象創建線程的時候,線程被命名為 named pool-(Number)-thread-(Number)。當為應用程序分析成百上千的線程的時候,如果線程依然用它們默認的名字,分析它們將變得非常困難,因為這是非常難以辨別這些線程來分析的。
因此,你被建議開發一個命名線程的規則當一個新線程被創建的時候。
當你使用 java.lang.Thread
創建線程,你可以通過創建參數給該線程定義個約定俗成的名字。
public Thread(Runnable target, String name);
public Thread(ThreadGroup group, String name); public Thread(ThreadGroup group, Runnable target, String name); public Thread(ThreadGroup group, Runnable target, String name, long stackSize);
當你使用 java.util.concurrent.ThreadFactory
創建線程的時候,你可以通過生成你自己的線程工廠來命名它,如果你不需要特別的功能性,你可以使用 MyThreadFactory
作為以下描述:
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicInteger; public class MyThreadFactory implements ThreadFactory { private static final ConcurrentHashMap<String, AtomicInteger> POOL_NUMBER = new ConcurrentHashMap<String, AtomicInteger>(); private final ThreadGroup group; private final AtomicInteger threadNumber = new AtomicInteger(1); private final String namePrefix; public MyThreadFactory(String threadPoolName) { if (threadPoolName == null) { throw new NullPointerException("threadPoolName"); } POOL_NUMBER.putIfAbsent(threadPoolName, new AtomicInteger()); SecurityManager securityManager = System.getSecurityManager(); group = (securityManager != null) ? securityManager.getThreadGroup() : Thread.currentThread().getThreadGroup(); AtomicInteger poolCount = POOL_NUMBER.get(threadPoolName); if (poolCount == null) { namePrefix = threadPoolName + " pool-00-thread-"; } else { namePrefix = threadPoolName + " pool-" + poolCount.getAndIncrement() + "-thread-"; } } public Thread newThread(Runnable runnable) { Thread thread = new Thread(group, runnable, namePrefix + threadNumber.getAndIncrement(), 0); if (thread.isDaemon()) { thread.setDaemon(false); } if (thread.getPriority() != Thread.NORM_PRIORITY) { thread.setPriority(Thread.NORM_PRIORITY); } return thread; } }
使用 MBean 獲取更多的細節信息
你可以使用 MBean 來獲取 ThreadInfo
對象。你也可以獲取更加多通過 thread dumps 不能獲取的信息。通過使用 ThreadInfo
。
ThreadMXBean mxBean = ManagementFactory.getThreadMXBean(); long[] threadIds = mxBean.getAllThreadIds(); ThreadInfo[] threadInfos = mxBean.getThreadInfo(threadIds); for (ThreadInfo threadInfo : threadInfos) { System.out.println( threadInfo.getThreadName()); System.out.println( threadInfo.getBlockedCount()); System.out.println( threadInfo.getBlockedTime()); System.out.println( threadInfo.getWaitedCount()); System.out.println( threadInfo.getWaitedTime()); }
你可以使用方法 ThreadInfo
來提取阻塞線程或者是等待線程花費的時間。並利用這一點,你也可以得到那些處於非活動狀態的時間異常長的線程列表。
總結
在本文中,我關注的是為開發人員提供了大量的多線程編程經驗,本素材可能是常識。對於經驗較少的開發人員來說,我覺得我直接跳過 thread dumps
,不提供足夠的關於 thread activities
的背景知識。這是由於我的知識缺乏,所以我不能很清晰的簡潔明了的解釋 thread activities
。我衷心的希望本文能給很多開發人員提供幫助。