理解Java死锁之死锁检测

作者:禹明明,叩丁狼高级讲师。原创文章,转载请注明出处。

看此文章前请先了解之前一篇文章 “Java死锁之理解死锁” 中的死锁示例

我们在开发中应该尽量避免死锁,但是如果真的有死锁产生那么我们怎么在一个复杂的项目中快速的找到死锁产生的原因呢?
我大概总结了一下常用的几种方式:

使用JConsole检测死锁

在我们的JAVA_HOME/bin目录下我们可以找到一个jdk提供的虚拟机监控工具叫Jconsole,双击打开连接你要查找的虚拟机进程,如图:
《理解Java死锁之死锁检测》
进入监控界面后点击左下角的检测死锁会检测出死锁的线程,点击具体的线程可以看到如下图死锁相关信息.
《理解Java死锁之死锁检测》

比如下图所示,我们可以看到

名称: B  //发生死锁的线程名称
状态: java.lang.Object@6b0eb7ca上的BLOCKED, 拥有者: A //线程B正在等待的锁, 被线程A所持有
总阻止数: 1, 总等待数: 1    

堆栈跟踪: 
test.B.run(TestThread.java:56) //该死锁发生在TestThread.java文件的第56行
   - 已锁定 java.lang.Object@6cf3ef8 //线程B持有的锁

这是的发生死锁的线程A的信息:

名称: A
状态: java.lang.Object@6cf3ef8上的BLOCKED, 拥有者: B //线程A正在等待的锁, 被线程B所持有
总阻止数: 1, 总等待数: 1

堆栈跟踪: 
test.A.run(TestThread.java:33)
   - 已锁定 java.lang.Object@6b0eb7ca  //线程A持有的锁

根据此信息我们可以快速定位到死锁发生的位置和锁对象,以及和其相关的线程从而解决问题。

使用JStack检测死锁

JStack 是JDK自带的命令行工具,主要用于线程Dump分析。(Dump文件是进程的内存镜像。保存的是进程的执行状态信息),该工具可以在JAVA_HOME/bin目录下找到,但是并非是图形化界面,所以不能双击运行
使用方式:
1.打开CMD命令提示符界面,输入jps 命令查看Java进程信息
《理解Java死锁之死锁检测》

2.找到要调试的JVM进程号 pid ,这里是15964
执行 jsatck -l pid (-l 参数可以打印出锁的相关信息)
PS:如果是真实项目可能会列出很多信息, 我们可以使用 jstack -l pid > D:dead.txt命令将所有信息导入到dead.txt中 然后在UE等一些比较强悍的文本编辑器中进行分析
《理解Java死锁之死锁检测》

信息比较多,我只截取最后这点跟死锁相关的信息
通过此信息,我们也可以分析出死锁发生的位置和相关线程,从而快速定位。

使用ThreadMXBean检测

java 中提供了可以检测死锁的工具类ThreadMXBean,我们可以用它来在项目运行时期使用代码去检测是否有死锁存在.
下面这段代码请参考”理解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 {
        //启动死锁线程
        new A("A").start();
        new B("B").start();
        //等待一段时间再执行死锁检测
        Thread.sleep(200);
        //获取到所有死锁线程的id
        long[] deadlockedThreads = mbean.findDeadlockedThreads();
        //遍历数组获取所有的死锁线程详细堆栈信息并打印
        for (long pid : deadlockedThreads) {
            //此方法获取不带有堆栈跟踪信息的线程数据
            //hreadInfo threadInfo = mbean.getThreadInfo(pid);
            //第二个参数指定转储多少项堆栈跟踪信息,设置为Integer.MAX_VALUE可以转储所有的堆栈跟踪信息
            ThreadInfo threadInfo = mbean.getThreadInfo(pid,Integer.MAX_VALUE);
            System.out.println(threadInfo);
        }
    }
}

打印出的死锁线程信息如下: 其中 “B” , “A” 是我的死锁示例中两个线程的名字

"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:检测死锁和获取堆栈信息是比较费性能的操作,如非必要不要经常执行

《理解Java死锁之死锁检测》

    原文作者:java锁
    原文地址: https://blog.csdn.net/wolfcode_cn/article/details/80728473
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞