理解Java死鎖之死鎖檢測

作者:禹明明,叩丁狼高級講師。原創(chuàng)文章,轉(zhuǎn)載請注明出處。

看此文章前請先了解之前一篇文章 "Java死鎖之理解死鎖" 中的死鎖示例

我們在開發(fā)中應(yīng)該盡量避免死鎖,但是如果真的有死鎖產(chǎn)生那么我們怎么在一個(gè)復(fù)雜的項(xiàng)目中快速的找到死鎖產(chǎn)生的原因呢?
我大概總結(jié)了一下常用的幾種方式:

使用JConsole檢測死鎖

在我們的JAVA_HOME/bin目錄下我們可以找到一個(gè)jdk提供的虛擬機(jī)監(jiān)控工具叫Jconsole,雙擊打開連接你要查找的虛擬機(jī)進(jìn)程,如圖:


jconsole1.jpg

進(jìn)入監(jiān)控界面后點(diǎn)擊左下角的檢測死鎖會檢測出死鎖的線程,點(diǎn)擊具體的線程可以看到如下圖死鎖相關(guān)信息.


jconsole2.jpg

比如下圖所示,我們可以看到

名稱: B  //發(fā)生死鎖的線程名稱
狀態(tài): java.lang.Object@6b0eb7ca上的BLOCKED, 擁有者: A //線程B正在等待的鎖, 被線程A所持有
總阻止數(shù): 1, 總等待數(shù): 1    

堆棧跟蹤: 
test.B.run(TestThread.java:56) //該死鎖發(fā)生在TestThread.java文件的第56行
   - 已鎖定 java.lang.Object@6cf3ef8 //線程B持有的鎖

這是的發(fā)生死鎖的線程A的信息:

名稱: A
狀態(tài): java.lang.Object@6cf3ef8上的BLOCKED, 擁有者: B //線程A正在等待的鎖, 被線程B所持有
總阻止數(shù): 1, 總等待數(shù): 1

堆棧跟蹤: 
test.A.run(TestThread.java:33)
   - 已鎖定 java.lang.Object@6b0eb7ca  //線程A持有的鎖


根據(jù)此信息我們可以快速定位到死鎖發(fā)生的位置和鎖對象,以及和其相關(guān)的線程從而解決問題。

使用JStack檢測死鎖

JStack 是JDK自帶的命令行工具,主要用于線程Dump分析。(Dump文件是進(jìn)程的內(nèi)存鏡像。保存的是進(jìn)程的執(zhí)行狀態(tài)信息),該工具可以在JAVA_HOME/bin目錄下找到,但是并非是圖形化界面,所以不能雙擊運(yùn)行
使用方式:
1.打開CMD命令提示符界面,輸入jps 命令查看Java進(jìn)程信息

jstack2.jpg

2.找到要調(diào)試的JVM進(jìn)程號 pid ,這里是15964
執(zhí)行 jsatck -l pid (-l 參數(shù)可以打印出鎖的相關(guān)信息)
PS:如果是真實(shí)項(xiàng)目可能會列出很多信息, 我們可以使用 jstack -l pid > D:dead.txt命令將所有信息導(dǎo)入到dead.txt中 然后在UE等一些比較強(qiáng)悍的文本編輯器中進(jìn)行分析

jstack1.jpg

信息比較多,我只截取最后這點(diǎn)跟死鎖相關(guān)的信息
通過此信息,我們也可以分析出死鎖發(fā)生的位置和相關(guān)線程,從而快速定位。

使用ThreadMXBean檢測

java 中提供了可以檢測死鎖的工具類ThreadMXBean,我們可以用它來在項(xiàng)目運(yùn)行時(shí)期使用代碼去檢測是否有死鎖存在.
下面這段代碼請參考"理解java線程死鎖"這篇文章,我們在其中添加了使用ThreadMXBean獲取死鎖信息的代碼

public class TestThread {
    
    public static final Object lock1 = new Object();//鎖對象1
    public static final Object lock2 = new Object();//鎖對象2
     //獲取ThreadMXBean
    public static ThreadMXBean mbean = ManagementFactory.getThreadMXBean();
    //測試方法
    public static void main(String[] args) throws InterruptedException {
        //啟動(dòng)死鎖線程
        new A("A").start();
        new B("B").start();
        //等待一段時(shí)間再執(zhí)行死鎖檢測
        Thread.sleep(200);
        //獲取到所有死鎖線程的id
        long[] deadlockedThreads = mbean.findDeadlockedThreads();
        //遍歷數(shù)組獲取所有的死鎖線程詳細(xì)堆棧信息并打印
        for (long pid : deadlockedThreads) {
            //此方法獲取不帶有堆棧跟蹤信息的線程數(shù)據(jù)
            //hreadInfo threadInfo = mbean.getThreadInfo(pid);
            //第二個(gè)參數(shù)指定轉(zhuǎn)儲多少項(xiàng)堆棧跟蹤信息,設(shè)置為Integer.MAX_VALUE可以轉(zhuǎn)儲所有的堆棧跟蹤信息
            ThreadInfo threadInfo = mbean.getThreadInfo(pid,Integer.MAX_VALUE);
            System.out.println(threadInfo);
        }
    }
}

打印出的死鎖線程信息如下: 其中 "B" , "A" 是我的死鎖示例中兩個(gè)線程的名字

"B" Id=14 BLOCKED on java.lang.Object@58d25a40 owned by "A" Id=13
    at test.B.run(TestThread.java:70)
    -  blocked on java.lang.Object@58d25a40

"A" Id=13 BLOCKED on java.lang.Object@726f3b58 owned by "B" Id=14
    at test.A.run(TestThread.java:47)
    -  blocked on java.lang.Object@726f3b58

PS:檢測死鎖和獲取堆棧信息是比較費(fèi)性能的操作,如非必要不要經(jīng)常執(zhí)行

WechatIMG7.jpeg
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容