當應用程序運行變慢或者發生故障時,可能通過分析java的Thread Dumps得到分析他們得到阻塞和存在瓶頸的線程。
線程堆棧是虛擬機中線程(包括鎖)狀態的一個瞬間狀態的快照,即系統在某一個時刻所有線程的運行狀態,包括每一個線程的調用堆棧,鎖的持有情況。主要包含的信息包括
1、線程名字,id,線程的數量等。
2、線程的運行狀態,鎖的狀態(鎖被哪個線程持有,哪個線程在等待鎖等)
3、調用堆棧包含完整的類名,所執行的方法,源代碼的行數等
線程棧是瞬時快照包含線程狀態以及調用關系,可以借助堆棧信息幫助分析很多性能問題。線程棧是瞬時記錄,所以沒有歷史消息的回溯,經常需要打印幾次做對比分析,並且在一般情況下都需要結合應用程序的日志進行問題定信。
1、系統cpu過高
2、性能瓶頸:如響應時間長但CPU資源並不高
3、系統運行越來越慢,響應時間長
4、系統掛起,長時間無響應或響應時間長
5、線程死鎖,死循環等
6、由於線程數量太多導致的內存溢出(如無法創建線程等)
Java線程原理
線程同步
多條線程之間可以同時執行,為了確保多線程在使用共享資源上面的通用性,使用線程同步保證在同一時間只能有一條線程可以訪問共享資源。
線程同步在Java中可以使用監視器。每個Java對象都有一個監視器,這個監視器只能被一個線程擁有。當一個線程要獲得另外線程擁有的監視器時,需要進入等待隊列直到線程釋放監視器。
線程的狀態
為了分析Thread Dump ,需要先了解線程的狀態。線程的狀態是在java.lang.Thread.State中。

1. 新建狀態(New) 新創建了一個線程對象。
2. 就緒狀態(Runnable) 線程對象創建后,其他線程調用了該對象的start()方法。該狀態的線程位於可運行線程池中,變得可運行,等待獲取CPU的使用權。
3. 運行狀態(Running) 就緒狀態的線程獲取了CPU,執行程序代碼。
4. 阻塞狀態(Blocked) 阻塞狀態是線程因為某種原因放棄CPU使用權,暫時停止運行。直到線程進入就緒狀態,才有機會轉到運行狀態。阻塞的情況分三種:
- 等待阻塞:運行的線程執行wait()方法,JVM會把該線程放入等待池中。
- 同步阻塞:運行的線程在獲取對象的同步鎖時,若該同步鎖被別的線程占用,則JVM會把該線程放入鎖池中。
- 其他阻塞:運行的線程執行sleep()或join()方法,或者發出了I/O請求時,JVM會把該線程置為阻塞狀態。當sleep()狀態超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程重新轉入就緒狀態。
5. 死亡狀態(Dead):線程執行完了或者因異常退出了run()方法,該線程結束生命周期。
Jstack使用的關鍵字描述的線程狀態與上邊線程不太一樣,所以可能理解上可能會出現混淆。雖然Java中的線程一樣有上述中描述的5種狀態,但在實際情況下線程新建狀態和死亡狀態持續很短,我們也並不太關心。大多時候我們關注的是運行狀態/阻塞狀態,

詳細過程可參考:
- 當執行new Thread(Runnabler)后,新創建出來的線程處於new狀態,這種線程不可能執行
- 當執行thread.start()后,線程處於runnable狀態,這種情況下只要得到CPU,就可以開始執行了。runnable狀態的線程,會接受JVM的調度,進入running狀態,但是具體何時會進入這個狀態,是隨機不可知的
- running狀態中的線程最為復雜,可能會進入runnable、waiting、timed_waiting、blocked、dead狀態:
- 如果CPU調度給了別的線程,或者執行了Thread.yield()方法,則進入runnable狀態,但是也有可能立刻又進入running狀態
- 如果執行了Thread.sleep(long),或者thread.join(long),或者在鎖對象上調用object.wait(long)方法,則會進入timed_waiting狀態
- 如果執行了thread.join(),或者在鎖對象上調用了object.wait()方法,則會進入waiting狀態
- 如果進入了同步方法或者同步代碼塊,沒有獲取鎖對象的話,則會進入blocked狀態
- 處於waiting狀態中的線程,如果是因為thread.join()方法進入等待的,在目標thread執行完畢之后,會回到runnable狀態;如果是因為object.wait()方法進入等待的話,在鎖對象執行object.notify()或者object.notifyAll()之后會回到runnable狀態
- 處於timed_waiting狀態中的線程,和waiting狀態中的差不多,只不過是設定時間到了,就會回到runnable狀態
- 處於blocked狀態中的線程,只有獲取了鎖之后,才會脫離阻塞狀態
- 當線程執行完畢,或者拋出了未捕獲的異常之后,會進入dead狀態,該線程結束
我們需要重點關注RUNNABLE、BLOCKED、WAITING和TIME_WAITING四種狀態,jstack打印的線程堆棧中也會時時出現。
1)BLOCKED:就是線程在等待獲取鎖進入同步塊或者同步方法中。兩個死鎖的線程即是Blocked。
2)WAITING:比BLOCKED狀態進步一些,指已經獲得鎖了,但由於有些條件不滿足,要自己等會,調用object.wait()方法。等條件滿足了,別的線程調用notify再叫我。另外也可以調用Thread.join()方法,顧名思義就是調用別的線程的join方法,讓別人join進來先執行,那我就只能等會了。但是由於wait()和notify()以及notifyAll()用於協調對共享資源的存取,所以必須在synchronized塊中使用。所以即便wait狀態的線程被notfiy喚醒了,也需要再次獲得鎖,所以喚醒后進入Blocked狀態。
3)TIMED_WAITING:類比WAITING,差異是不需要notify()或者notifyAlL()方法喚醒,時間到了自己醒了。另外sleep比較好理解,就是讓當前線程睡一會,與wait的區別是它不釋放鎖。
4)RUNNABLE不用多說,在JAVA虛擬機中已經在運行,但是在等待操作系統資源,比如CPU時間片。
相關鏈接:
https://blog.csdn.net/rachel_luo/article/details/8920596
https://blog.csdn.net/zxp_cpinfo/article/details/54971115
