簡介:
有些時候我們需要查看下jvm中的線程執行情況,比如,發現服務器的CPU的負載突然增高了、出現了死鎖、死循環等,我們該如何分析呢?
由於程序是正常運行的,沒有任何的輸出,從日志方面也看不出什么問題,所以就需要看下jvm的內部線程的執行情況,然后再進行分析查找出原因。
這個時候,就需要借助於jstack命令了,jstack的作用是將正在運行的jvm的線程情況進行快照,並且打印出來;
在Java中線程的狀態一共被分成6種:
初始狀態:New,線程對象創建出來后,沒有調用start方法,線程處於初始狀態
運行狀態:包含了就緒狀態和運行狀態
1.就緒狀態:Ready,調用了Start方法,等待CPU分配資源
2.運行狀態:RUNNING,CPU分配資源給該線程,該線程處於運行狀態
阻塞狀態 BLOCKED:
線程獲取資源,如果資源獲取成功則正常運行,如果資源獲取失敗,就處於阻塞狀態,等待什么時候獲取到資源再變為運行狀態
等待狀態 WAITING:線程手動調用了wait()方法,或者join()方法,這些方法都是主動進入等待狀態,等待狀態會將CPU資源讓渡
需要其他線程手動喚醒,notify(),notifyAll()喚起所有的等待線程
超時等待狀態 TIMED_WAITING:與等待狀態相同,都是主動進入等待,也是需要其他線程喚醒,但是區別在與超時等待,如果超過了等待時間,則自動喚醒
Thread.sleep(2000),在休眠等待時間內會將CPU資源讓渡,然后等待時間結束自動進入運行狀態
終止狀態 DIED:線程結束之后的狀態
案例:解決死鎖問題
如果在生產環境發生了死鎖,我們將看到的是部署的程序沒有任何反應了,這個時候我們可以借助jstack進行分析,下面我們實戰下查找死鎖的原因。
構建死鎖
編寫代碼,啟動2個線程,Thread1拿到了obj1鎖,准備去拿obj2鎖時,obj2已經被Thread2鎖定,所以發送了死鎖。
public class LockTest { //定義資源 private static Object obj1=new Object(); private static Object obj2=new Object(); //線程A:先獲取到資源1,然后休眠2s,再獲取資源2 private static class ThreadA implements Runnable { @Override public void run() { synchronized (obj1) { System.out.println("ThreadA獲取到了OBJ1資源"); try { //休眠2s,因為我們要將CPU資源讓渡出去,這樣線程B就可以先搶占obj2資源 Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (obj2) { System.out.println("ThreadA獲取到了OBJ2資源"); } } } } private static class ThreadB implements Runnable{ @Override public void run() { synchronized (obj2){ System.out.println("ThreadB獲取到了OBJ2資源"); try { //休眠2s,因為我們要將CPU資源讓渡出去,這樣線程B就可以先搶占obj2資源 Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (obj1){ System.out.println("ThreadA獲取到了OBJ1資源"); } } } } public static void main(String[] args) { new Thread(new ThreadA()).start(); new Thread(new ThreadB()).start(); } }
運行出現死鎖問題
檢查狀態
查看線程運行情況
主要看如下:
在輸出的信息中,已經看到,發現了1個死鎖
可以清晰的看到:
Thread-1獲取了 <0x0517c5b0> 的鎖,等待獲取 <0x0517c5a8>這個鎖
Thread-0獲取了 <0x0517c5a8> 的鎖,等待獲取 <0x0517c5b0>這個鎖
由此可見,發生了死鎖