怎樣分析 JAVA 的 Thread Dumps

注: 該文章的原文是由 Tae Jin Gu 編寫,原文地址爲 How to Analyze Java Thread Dumpsjava

當有障礙,或者是一個基於 JAVA 的 WEB 應用運行的比預期慢的時候,咱們須要使用 thread dumps。若是對於你來講, thread dumps 是很是複雜的,這篇文章或許能對你有所幫助。在這裏我將解釋在 JAVA 中什麼是 threads,他們的類型,怎麼被建立的,怎樣管理它們,你怎樣從正在運行的應用中 dump threads,最後你能夠怎樣分析它以及肯定瓶頸或者是阻塞線程。本文來自於 JAVA 應用程序長期調試經驗的結果。linux

Java and Thread

一個 web 服務器使用幾十到幾百個線程來處理大量併發用戶,若是一個或多個線程使用相同的資源,線程之間的競爭就不可避免了,而且有時候可能會發生死鎖。web

Thread contention 是一個線程等待鎖的一個狀態,這個鎖被另一個線程持有,等待被釋放,不一樣的線程頻繁訪問 WEB 應用的共享資源。例如,記錄一條日誌,線程嘗試記錄日誌以前必須先獲取鎖來訪問共享資源。apache

死鎖是線程競爭的一個特殊狀態,一個或是多個線程在等待其餘線程完成它們的任務爲了完成它們本身的任務。編程

線程競爭會引發各類不一樣的問題,爲了分析這些這些問題,你須要使用 dump threadsdump threads 能給你提供每一個線程的精確狀態信息。服務器

JAVA 線程的背景資料

線程同步

一個線程能夠與其餘線程在同一時間內被處理。爲了確保一致性,當多個線程試圖使用共享資源的時候,經過使用 hread synchronization 在同一時間內,應該只有一個線程能訪問共享資源多線程

JAVA 中的線程同步可使用監視器,每一個 JAVA 對象都有一個單獨的監視器,這個監視器僅僅只能被一個線程擁有,對於擁有一個由不一樣的線程所擁有的監視器的線程,確實須要在隊列中等待,以便其餘線程釋放它的監視器。併發

線程狀態

爲了分析一個 thread dump 文件,你須要知道線程狀態。線程狀況在 java.lang.Thread.State 中闡明瞭。oracle

此處輸入圖片的描述

圖1:線程狀態app

  • NEW:線程剛被建立,可是尚未被處理。
  • RUNNABLE:線程佔用了 CPU 而且處理了一個任務。(或是是在等待狀態因爲操做系統的資源分配)
  • BLOCKED:該線程正在等待另外的不一樣的線程釋放鎖,以便獲取監視器鎖
  • WAITING:該線程正在等待,經過使用了 wait, join 或者是 park 方法
  • TIMED_WAITING:該線程正在等待,經過使用了 sleep, wait, join 或者是 park 方法。(這個與 WAITING 不一樣是經過方法參數指定了最大等待時間,WAITING 能夠經過時間或者是外部的變化解除)

線程類型

JAVA 的線程類型分爲如下兩種:

  1. daemon threads;
  2. 非 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-2BLOCKED_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 利用率高的異常

  1. 提取獲取最高 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)。

  1. 而後獲取進程的 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。我衷心的但願本文能給不少開發人員提供幫助。

相關文章
相關標籤/搜索