关于DeadLock(死锁)的总结与Java代码实现

用交通状况做比喻,死锁 = 十字路口交叉相行的车把对方的路给堵死了,造成大堵车情况。

1. 死锁产生及其检测算法

要发生死锁还得有以下条件:

  • 互斥:每个资源要么已经分配给了一个进程,要么就是可用的。
  • 占有和等待:已经得到了某个资源的进程可以再请求新的资源。

死锁检测算法是通过检测有向图是否存在环来实现,从一个节点出发进行深度优先搜索,对访问过的节点进行标记,如果访问了已经标记的节点,就表示有向图存在环,也就是检测到死锁的发生。

2. 死锁的解决方式

《并发编程的艺术》中给出的解决方法:

  • 避免一个线程同时获取多个锁。
  • 避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源。
  • 尝试使用定时锁,使用lock.tryLock(timeout)来替代使用内部锁机制。
  • 对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况。
    以上四种其实是预防方法,如果是已发生死锁,可以有以下几种恢复方式:
  • 利用抢占恢复
  • 利用回滚恢复
  • 通过杀死进程恢复

3. DeadLock的代码演示

本篇文章中演示死锁的场景,是在一些更为 复杂的场景中可能会遇到的问题。比如t1拿到锁之后,因为一些异常情况没有释放锁 (死循环)。又或者是t1拿到一个数据库锁,释放锁的时候抛出了异常,没释放掉。
一旦出现死锁,业务是可感知的,因为不能继续提供服务了,那么只能通过dump线程查看 到底是哪个线程出现了问题,以下线程信息告诉我们是DeadLockDemo类的第42行和第31行引 起的死锁
1.创建t1和t2两个线程,互相加锁

public class NormalLock {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
           //给原始类型int的Class对象方法加锁 
            synchronized (Integer.class) {
                System.out.println("t1 Integer");
                synchronized (Long.class) {
                    System.out.println("t1 Long");
                }
            }
        });
        t1.start();
        Thread t2 = new Thread(() -> {
            synchronized (Long.class) {
                System.out.println("t2 Long");
                synchronized (Integer.class) {
                    System.out.println("t2 Integer");
                }
            }
        });
        t2.start();
    }
}

结果:
在这里插入图片描述
2,给t1一定的线程时间

public class DeadLock {
    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            synchronized (Integer.class) {
                System.out.println("t1 Integer");
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (Long.class) {
                    System.out.println("t1 Long");
                }
            }
        });
        t1.start();
        Thread t2 = new Thread(() -> {
            synchronized (Long.class) {
                System.out.println("t2 Long");
                synchronized (Integer.class) {
                    System.out.println("t2 Integer");
                }
            }
        });
        t2.start();
    }
}

结果就是一直卡住:
在这里插入图片描述
可以看出,要避免一个线程同时获取多个锁。

  • 就是避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源。
  • 尝试使用定时锁,使用lock.tryLock(timeout)来替代使用内部锁机制。 ·对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况。
    参考文章:
    github—计算机系统之死锁

猜你喜欢

转载自blog.csdn.net/smile001isme/article/details/106195425