死锁检测的常用3种方法

什么是死锁

死锁(Dead Lock)是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。
在这里插入图片描述

死锁示例

接下来,我们先来演示一下 Java 中最简单的死锁,我们创建两个锁和两个线程,让线程 1 先拥有锁 o1,然后在 2s 后尝试获取锁 o2,同时我们启动线程 2,让它先拥有锁 o2,然后在 2s 之后尝试获取锁 o1,这时就会出现相互等待对方释放锁的情况,从而造成死锁的问题。
具体代码如下:

public class DeadLock {
    public static void main(String[] args) {
        Thread thread1 = new Thread(new DeadLockDemo(true));
        Thread thread2 = new Thread(new DeadLockDemo(false));
        thread1.start();
        thread2.start();
    }
}


class DeadLockDemo implements Runnable {

    static Object o1 = new Object();
    static Object o2 = new Object();
    boolean flag;

    public DeadLockDemo(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        if (flag) {
            synchronized (o1) {
                System.out.println("线程1进入o1");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程1等待进入o2");
                synchronized (o2) {
                    System.out.println(Thread.currentThread().getName() + "线程进入o2");
                }
            }
        } else {
            synchronized (o2) {
                System.out.println("线程2进入o2");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("线程2等待进入o1");
                synchronized (o1) {
                    System.out.println(Thread.currentThread().getName() + "线程进入o1");
                }
            }
        }
    }
}

程序执行结果:
在这里插入图片描述
从上述结果可以看出,线程 1 和线程 2 都在等待对方释放锁,这样就造成了死锁问题。

死锁产生原因

通过以上示例,我们可以得出结论,要产生死锁需要满足以下 4 个条件:

互斥条件:指运算单元(进程、线程或协程)对所分配到的资源具有排它性,也就是说在一段时间内某个锁资源只能被一个运算单元所占用。
请求和保持条件:指运算单元已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它运算单元占有,此时请求运算单元阻塞,但又对自己已获得的其它资源保持不放。
不可剥夺条件:指运算单元已获得的资源,在未使用完之前,不能被剥夺。
环路等待条件:指在发生死锁时,必然存在运算单元和资源的环形链,即运算单元正在等待另一个运算单元占用的资源,而对方又在等待自己占用的资源,从而造成环路等待的情况。
只有以上 4 个条件同时满足,才会造成死锁问题。

死锁排查

如果程序出现死锁问题,可通过以下 3种方案中的任意一种进行分析和排查。
方案一:jstack
在使用 jstack 之前,我们需要先通过 jps -l得到运行程序的进程 ID,使用方法如下:
在这里插入图片描述
有了进程 ID(PID)之后,我们就可以使用“jstack -l PID”来发现死锁问题了,如下图所示:
在这里插入图片描述

方案二:jconsole
jconsole工具位于jdk的bin目录下,如图:
在这里插入图片描述
双击进入,选择要调试的程序,如下图所示:
在这里插入图片描述
会出现如下提示,选择不安全的链接
在这里插入图片描述
点击线程栏目,然后点下面死锁,就会检测出来了
在这里插入图片描述

方案三:jvisualvm
jvisualvm 也在 JDK 的 bin 目录中,同样是双击打开:
在这里插入图片描述
点击线程栏目,如果发生死锁,会自动提示如下:
在这里插入图片描述
点击线程dump:

在这里插入图片描述
以上是jdk8以上版本排查线程死锁的常用三种方案,还有一种是根据jmc(Oracle Java Mission Control )排查,jmc是一个对 Java 程序进行管理、监控、概要分析和故障排查的工具套件,它也是在 JDK 的 bin 目录中。但是jdk1.8之后,jmc就已经不随着jdk一起发出去了,如果想要使用jmc对jvm进行监控,这时候需要手工下载jmc,然后在本地运行。oracle官网比较坑,现在只有jmc8.1版本下载,它需要jdk11,但作者暂时还没亲自试过,所以先不写了。

  • 5
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值