問題:java
針對線上多線程死鎖、阻塞,跑着跑着就卡住了數據庫
查看線上線程池的狀態apache
jstack用於生成java虛擬機當前時刻的線程快照。網絡
線程快照是當前java虛擬機內每一條線程正在執行的方法堆棧的集合,生成線程快照的主要目的是定位線程出現長時間停頓的緣由,如線程間死鎖、死循環、請求外部資源致使的長時間等待等。多線程
jstack:java堆棧跟蹤工具
jstack用於生成java虛擬機當前時刻的線程快照。oracle
線程快照是當前java虛擬機內每一條線程正在執行的方法堆棧的集合,生成線程快照的主要目的是定位線程出現長時間停頓的緣由,如線程間死鎖、死循環、請求外部資源致使的長時間等待等。jsp
命令格式:工具
jstack [ option ] pid
jstack [ option ] executable core
jstack [ option ] [server-id@]remote-hostname-or-IP
- 執行
jstack
命令,將獲得進程的堆棧信息。我通常使用jstack -l pid
來獲得長列表,顯示其詳細信息。
- 有時線程掛起的時候,須要執行
jstack -F pid
來獲取。
- 在實際運行中,每每一次 dump的信息,還不足以確認問題。建議產生三次 dump信息,若是每次 dump都指向同一個問題,咱們才肯定問題的典型性。
- 堆棧信息只是一種參考,一些正常RUNNING的線程,因爲複雜網絡環境和IO的影響,也是有問題的,用jstack就沒法定位,須要結合對業務代碼的理解。
使用:查看服務進程的線程狀況ui
taskExecutor- 爲線程詞的名稱spa
java.lang.Thread.State 線程的狀態
具體爲線程的執行到什麼動做 代碼行數
一個Thread對象能夠有多個狀態,在java.lang.Thread.State中,總共定義六種狀態:
一、NEW
線程剛剛被建立,也就是已經new過了,可是尚未調用start()方法,jstack命令不會列出處於此狀態的線程信息
2、RUNNABLE #java.lang.Thread.State: RUNNABLE
RUNNABLE這個名字很具備欺騙性,很容易讓人誤覺得處於這個狀態的線程正在運行。事實上,這個狀態只是表示,線程是可運行的。咱們已經無數次提到過,一個單核CPU在同一時刻,只能運行一個線程。
三、BLOCKED # java.lang.Thread.State: BLOCKED (on object monitor)
線程處於阻塞狀態,正在等待一個monitor lock。一般狀況下,是由於本線程與其餘線程公用了一個鎖。其餘在線程正在使用這個鎖進入某個synchronized同步方法塊或者方法,而本線程進入這個同步代碼塊也須要這個鎖,最終致使本線程處於阻塞狀態。
4、WAITING
等待狀態,調用如下方法可能會致使一個線程處於等待狀態:
Object.wait 不指定超時時間 # java.lang.Thread.State: WAITING (on object monitor)
Thread.join with no timeout
LockSupport.park #java.lang.Thread.State: WAITING (parking)
例如:對於wait()方法,一個線程處於等待狀態,一般是在等待其餘線程完成某個操做。本線程調用某個對象的wait()方法,其餘線程處於完成以後,調用同一個對象的notify或者notifyAll()方法。Object.wait()方法只可以在同步代碼塊中調用。調用了wait()方法後,會釋放鎖。
5、TIMED_WAITING
線程等待指定的時間,對於如下方法的調用,可能會致使線程處於這個狀態:
Thread.sleep #java.lang.Thread.State: TIMED_WAITING (sleeping)
Object.wait 指定超時時間 #java.lang.Thread.State: TIMED_WAITING (on object monitor)
Thread.join with timeout
LockSupport.parkNanos #java.lang.Thread.State: TIMED_WAITING (parking)
LockSupport.parkUntil #java.lang.Thread.State: TIMED_WAITING (parking)
6、TERMINATED
線程終止。
在dump 文件裏,寫法可能不太同樣:
- 死鎖,Deadlock(重點關注)
- 執行中,Runnable
- 等待資源,Waiting on condition(重點關注)
- 等待獲取監視器,Waiting on monitor entry(重點關注)
- 對象等待中,Object.wait() 或 TIMED_WAITING
- 暫停,Suspended
- 阻塞,Blocked(重點關注)
- 中止,Parked
Runnable
線程正在運行中。
通常指該線程正在執行狀態中,該線程佔用了資源,正在處理某個請求,有可能正在傳遞SQL到數據庫執行,有可能在對某個文件操做,有可能進行數據類型等轉換。
Deadlock
-
"t2" prio=6 tid=0x02bcf000 nid=0xc70 waiting for monitor entry [0x02f6f000]
-
java.lang.Thread.State: BLOCKED (on object monitor)
-
at com.demo.DeadLock
$2.run(DeadLock.java:40)
-
- waiting to lock <0x22a297a8> (a java.lang.Object)
-
- locked <0x22a297b0> (a java.lang.Object)
-
Locked ownable synchronizers:
-
-
"t1" prio=6 tid=0x02bce400 nid=0xba0 waiting for monitor entry [0x02f1f000]
-
java.lang.Thread.State: BLOCKED (on object monitor)
-
at com.demo.DeadLock
$1.run(DeadLock.java:25)
-
- waiting to lock <0x22a297b0> (a java.lang.Object)
-
- locked <0x22a297a8> (a java.lang.Object)
-
Locked ownable synchronizers:
-
上面是一個典型的死鎖堆棧,t1線程lock在地址0x22a297a8
,同時t2線程在waiting to lock
這個地址。
更有意思的是,堆棧也記錄了發生死鎖的代碼行數,這對咱們定位問題起到很大的幫助。
Wait on condition
等待資源,或等待某個條件的發生。具體緣由需結合 stacktrace來分析。
常見狀況是該線程在 sleep,等待 sleep的時間到了時候,將被喚醒。
關鍵字:TIMED_WAITING
,sleeping
,parking
。TIMED_WAITING多是調用了有超時參數的wait所引發的。
parking指線程處於掛起中。
下面是一個典型的sleep引發的Wait on condition
。
-
"thread-1" prio=10 tid=0x00007fbe985cd000 nid=0x7bc6 waiting on condition [0x00007fbe65848000]
-
java.lang.Thread.State: TIMED_WAITING (sleeping)
-
at java.lang.Thread.sleep(Native Method)
-
at com.xxx.MonitorManager
$2.run(MonitorManager.java:124)
-
at java.util.concurrent.ThreadPoolExecutor
$Worker.runTask(ThreadPoolExecutor.java:895)
-
at java.util.concurrent.ThreadPoolExecutor
$Worker.run(ThreadPoolExecutor.java:918)
-
at java.lang.Thread.run(Thread.java:662)
-
"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.xxx.ThreadIoWaitState
$IoWaitHandler2.run(ThreadIoWaitState.java:89)
-
at java.lang.Thread.run(Thread.java:662)
Waiting on monitor entry和Object.wait()
意味着線程在等待進入一個臨界區
Monitor是 Java中用以實現線程之間的互斥與協做的主要手段,它能夠當作是對象或者 Class的鎖。
每個對象都有,也僅有一個 monitor。
- 全部期待得到鎖的線程,在鎖已經被其它線程擁有的時候,這些期待得到鎖的線程就進入了
Object Lock
的entry set
區域。
- 全部曾經得到過鎖,可是因爲其它必要條件不知足而須要wait的時候,線程就進入了
Object Lock
的wait set
區域 。
- 在
wait set
區域的線程得到notify/notifyAll
通知的時候,隨機的一個Thread(notify)或者是所有的Thread(notifyALL)從Object Lock的wait set區域進入了entry set中。
- 在當前擁有鎖的線程釋放掉鎖的時候,處於該
Object Lock
的entryset
區域的線程都會搶佔該鎖,可是隻能有任意的一個Thread能取得該鎖,而其餘線程依然在entry set
中等待下次來搶佔到鎖以後再執行。
看下面的堆棧,線程在等待數據庫鏈接池返回一個可用的連接
-
" 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 xxx.getTodayCount(ServiceCnt.java:111)
-
at xxx.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 xxx.ServiceCnt.getTodayCount(ServiceCnt.java:111)
-
at xxx.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 xxx.Cue_1700c.GetNationList(Cue_1700c.java:66)
-
at org.apache.jsp.cue_1700c_jsp._jspService(cue_1700c_jsp.java:120)
Blocked
線程阻塞,是指當前線程執行過程當中,所須要的資源長時間等待卻一直未能獲取到,被容器的線程管理器標識爲阻塞狀態,能夠理解爲等待資源超時的線程。
若是線程處於Blocked狀態,可是緣由不清楚。可使用jstack -m pid
獲得線程的mixed信息。
-
----------------- t@13 -----------------
-
0xff31e8b8 ___lwp_cond_wait + 0x4
-
0xfea8c810 void ObjectMonitor::EnterI(Thread*) + 0x2b8
-
0xfeac86b8 void ObjectMonitor::enter2(Thread*) + 0x250
例如,以上信息代表,線程在嘗試進入同步塊時阻塞了。