【知识总结】死锁

概念

死锁是指两个或多个以上的进程在执行过程中,因争夺资源而造成一种互相等待的现象,若无外力干涉那他们都将无法推进下去。如果资源充足,进程的资源请求都能够得到满足,死锁出现的可能性就很低,否则就会因争夺有限的资源而陷入死锁。

持有自己的锁,还妄图要别人的锁。

【知识总结】死锁_第1张图片

死锁产生的4个必要条件(缺一不可)

想象你来到巴黎卢浮宫博物馆,想要排队看看《蒙娜丽莎的微笑》:

  • 互斥:一幅画一次只能一个人看。(某个资源只能互斥的使用,例如磁盘、物理内存)
  • 请求保持:

    1. 请求:张三排队轮到他看蒙娜丽莎的同时,他很贪心,要看蒙娜丽莎的同时,要看梵高的向日葵(进程在使用已申请资源时,申请使用其他互斥资源)
    2. 保持:如果不给张三看向日葵,那么张三就不走(进程保持对已分配资源的占有)
  • 不可剥夺:如果李四正在看向日葵,张三看不了,那么张三就一直堵在蒙娜丽莎前面,不给其他人看(进行已经申请的资源在没释放前不可被剥夺)
  • 循环等待:李四也不满足于看向日葵,也要同时看蒙娜丽莎,而张三占用了蒙娜丽莎,同时也要看向日葵。导致两人谁也不走,陷入了僵局

死锁预防(破坏死锁的条件)

通过破坏死锁的四大必要条件,来预防死锁的发生:

  • 互斥:有些资源本身就是互斥的,无法改变,有的时候是代码设计导致的某些变量互斥了。就像这里的画一样,让多个人一起看不就解决了。
  • 请求保持:一次性分配进程的所有资源。每个人参观必须实现申明好想看的画,然后一次性分配所需的画,不能中途提新的要求。
  • 不可剥夺:进程等待新的资源的时候,将自己的资源先释放,等有新的资源后,操作系统才会唤醒它。让这个想看同时两幅画的人先滚到一边去,等两幅画都空了再给他。
  • 循环等待:将资源编号,每次只能从小到大申请。将向日葵编号为 1,蒙娜丽莎编号为 2,必须先申请参观 1,持有 1 的同时才能申请 2。

解除正在死锁的状态

剥夺资源:从其他进程剥夺足够数量的资源给死锁进程,已解除死锁状态。

撤销进程:可以直接撤销死锁进程,直至有足够的资源可用为止。

死锁代码

我们创建了一个资源类,然后让两个线程分别持有自己的锁同时在尝试获取别人的锁,就会出现死锁现象。

class HoldLockThread implements Runnable {
    private String lockA;
    private String lockB;

    //构造函数
    public HoldLockThread(String lockA, String lockB) {
        this.lockA = lockA;
        this.lockB = lockB;
    }
    
    //run方法
    @Override
    public void run() {
    synchronized (lockA) {
        System.out.println(Thread.currentThread().getName()+"自己持有:"+lockA +" 尝试获得:"+lockB);
        synchronized (lockB) {
            System.out.println(Thread.currentThread().getName()+"自己持有:"+lockB +" 尝试获得:"+lockA);
        }
    }
}
}
public class DeadLockDemo {
    public static void main(String[] args) {
        String lockA = "lockA";
        String lockB = "lockB";

        new Thread(new HoldLockThread(lockA, lockB), "线程1").start();
        new Thread(new HoldLockThread(lockB, lockA), "线程2").start();
    }
}

运行结果,发现main线程无法结束

如何排查死锁

  • 通过jdk工具jps、jstack排查死锁问题
    1. 使用 jps 命令查看运行的程序。这是jdk提供的一个查看当前java进程的工具。

      //在IDEA打开Terminal,输入
      jps -l

      我们能看到DeadLockDemo这个类,一直在运行。

      【知识总结】死锁_第2张图片

    2. 使用 jstack 查看线程堆栈信息。

      jstack 3264            //3264 是上面一直运行的进程的编号

      【知识总结】死锁_第3张图片

      通过查看最后一行,我们看到 Found 1 deadlock,即存在一个死锁。

  • 通过jdk提供的工具 VisualVM 排查死锁问题
VisuaVM:jdk提供的一个排查 java 问题的工具。可用监控程序的性能、查看JVM配置信息、线程堆栈的信息。
  • 通过jdk提供的工具jconsole排查死锁问题
jconsole:jdk提供的一个可视化的工具,方便排查程序的一些问题,如:程序内存溢出、死锁问题等等。

在本地线程处找到我们的进程,进行连接。连接后在窗口中查看线程堆栈信息,有个 “检测死锁” 的按钮,可用看到程序的死锁信息。

你可能感兴趣的:(java死锁)