jstack 線程狀態詳解
jatsck 用法
#jstack -h
Usage:
jstack [-l] <pid>
(to connect to running process)
jstack -F [-m] [-l] <pid>
(to connect to a hung process)
jstack [-m] [-l] <executable> <core>
(to connect to a core file)
jstack [-m] [-l] [server_id@]<remote server IP or hostname>
(to connect to a remote debug server)
Options:
-F to force a thread dump. Use when jstack <pid> does not respond (process is hung)
-m to print both java and native frames (mixed mode)
-l long listing. Prints additional information about locks
-h or -help to print this help message
線程狀態
狀態 | 含義 | 觸發方法 | 線程動作(線程狀態產生的原因) | 備註 |
---|---|---|---|---|
NEW | Thread state for a thread which has not yet started. | 不會出現在Dump中 | ||
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 | ||
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 Object.wait. | waiting for monitor entry | ||
WAITING | Thread state for a waiting thread. | Object.wait with no timeout => WAITING (on object monitor) | in Object.wait() | A thread that has called Object.wait() on an object is waiting for another thread to call Object.notify() or Object.notifyAll() on that object. |
Thread.join with no timeout=> WAITING (on object monitor) | in Object.wait() | A thread that has called Thread.join() is waiting for a specified thread to terminate. | ||
LockSupport.park=> WAITING(parking) | waiting on condition | |||
TIMED_WAITING | Thread state for a waiting thread with a specified waiting time. | Thread.sleep=> TIMED_WAITING (sleeping) | waiting on condition | |
Object.wait with timeout=> TIMED_WAITING (on object monitor) | in Object.wait() | |||
Thread.join with timeout=> TIMED_WAITING (on object monitor) | in Object.wait() | |||
LockSupport.parkNanos=> TIMED_WAITING (parking) | waiting on condition | |||
LockSupport.parkUntil=> TIMED_WAITING (parking) | waiting on condition | |||
TERMINATED | Thread state for a terminated thread. The thread has completed execution. | 不會出現在Dump中 |
一圖以庇之
系統線程狀態 (Native Thread Status)
系統線程有如下狀態:
-
deadlock
死鎖線程,一般指多個線程調用期間進入了相互資源佔用,導致一直等待無法釋放的情況。 -
runnable
一般指該線程正在執行狀態中,該線程佔用了資源,正在處理某個操作,如通過SQL語句查詢數據庫、對某個文件進行寫入等。 -
blocked
線程正處於阻塞狀態,指當前線程執行過程中,所需要的資源長時間等待卻一直未能獲取到,被容器的線程管理器標識爲阻塞狀態,可以理解爲等待資源超時的線程。 -
waiting on condition
線程正處於等待資源或等待某個條件的發生,具體的原因需要結合下面堆棧信息進行分析。- 如果堆棧信息明確是應用代碼,則證明該線程正在等待資源,一般是大量讀取某種資源且該資源採用了資源鎖的情況下,線程進入等待狀態,等待資源的讀取,或者正在等待其他線程的執行等。
- 如果發現有大量的線程都正處於這種狀態,並且堆棧信息中得知正等待網絡讀寫,這是因爲網絡阻塞導致線程無法執行,很有可能是一個網絡瓶頸的徵兆:
- 網絡非常繁忙,幾乎消耗了所有的帶寬,仍然有大量數據等待網絡讀寫;
- 網絡可能是空閒的,但由於路由或防火牆等原因,導致包無法正常到達;
所以一定要結合系統的一些性能觀察工具進行綜合分析,比如netstat統計單位時間的發送包的數量,看是否很明顯超過了所在網絡帶寬的限制;觀察CPU的利用率,看系統態的CPU時間是否明顯大於用戶態的CPU時間。這些都指向由於網絡帶寬所限導致的網絡瓶頸。
- 還有一種常見的情況是該線程在 sleep,等待 sleep 的時間到了,將被喚醒。
-
waiting for monitor entry 或 in Object.wait()
Moniter 是Java中用以實現線程之間的互斥與協作的主要手段,它可以看成是對象或者class的鎖,每個對象都有,也僅有一個 Monitor。
從上圖可以看出,每個Monitor在某個時刻只能被一個線程擁有,該線程就是 “Active Thread”,而其他線程都是 “Waiting Thread”,分別在兩個隊列 "Entry Set"和"Waint Set"裏面等待。其中在 “Entry Set” 中等待的線程狀態是 waiting for monitor entry,在 “Wait Set” 中等待的線程狀態是 in Object.wait()。
(1)"Entry Set"裏面的線程。
我們稱被 synchronized 保護起來的代碼段爲臨界區,對應的代碼如下:
synchronized(obj) { }COPY
當一個線程申請進入臨界區時,它就進入了 “Entry Set” 隊列中,這時候有兩種可能性:
該Monitor不被其他線程擁有,"Entry Set"裏面也沒有其他等待的線程。本線程即成爲相應類或者對象的Monitor的Owner,執行臨界區裏面的代碼;此時在Thread Dump中顯示線程處於 “Runnable” 狀態。
該Monitor被其他線程擁有,本線程在 “Entry Set” 隊列中等待。此時在Thread Dump中顯示線程處於 “waiting for monity entry” 狀態。
臨界區的設置是爲了保證其內部的代碼執行的原子性和完整性,但因爲臨界區在任何時間只允許線程串行通過,這和我們使用多線程的初衷是相反的。如果在多線程程序中大量使用synchronized,或者不適當的使用它,會造成大量線程在臨界區的入口等待,造成系統的性能大幅下降。如果在Thread Dump中發現這個情況,應該審視源碼並對其進行改進。
(2)"Wait Set"裏面的線程
當線程獲得了Monitor,進入了臨界區之後,如果發現線程繼續運行的條件沒有滿足,它則調用對象(通常是被synchronized的對象)的wait()方法,放棄Monitor,進入 "Wait Set"隊列。只有當別的線程在該對象上調用了 notify()或者notifyAll()方法,"Wait Set"隊列中的線程纔得到機會去競爭,但是隻有一個線程獲得對象的Monitor,恢復到運行態。"Wait Set"中的線程在Thread Dump中顯示的狀態爲 in Object.wait()。通常來說,
通常來說,當CPU很忙的時候關注 Runnable 狀態的線程,反之則關注 waiting for monitor entry 狀態的線程。