死锁和死锁代码实现

死锁概念

什么是死锁

死锁(Deadlock)描述的是这样一种情况:多个进程/线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于进程/线程被无限期地阻塞,因此程序不可能正常终止。

产生死锁的四个必要条件是什么

  1. 互斥:资源必须处于非共享模式,即一次只有一个进程可以使用。如果另一进程申请该资源,那么必须等待直到该资源被释放为止。
  2. 占有并等待:一个进程至少应该占有一个资源,并等待另一资源,而该资源被其他进程所占有。
  3. 非抢占:资源不能被抢占。只能在持有资源的进程完成任务后,该资源才会被释放。
  4. 循环等待 :有一组等待进程 {P0, P1,..., Pn}P0 等待的资源被 P1 占有,P1 等待的资源被 P2 占有,......,Pn-1 等待的资源被 Pn 占有,Pn 等待的资源被 P0 占有。

注意 :这四个条件是产生死锁的 必要条件 ,也就是说只要系统发生死锁,这些条件必然成立,而只要上述条件之一不满足,就不会发生死锁。

解决死锁的方法

解决死锁的方法可以从多个角度去分析,一般的情况下,有预防,避免,检测和解除四种

  • 预防 是采用某种策略,限制并发进程对资源的请求,从而使得死锁的必要条件在系统执行的任何时间上都不满足。
  • 避免 则是系统在分配资源时,根据资源的使用情况提前做出预测 ,从而避免死锁的发生
  • 检测 是指系统设有专门的机构,当死锁发生时,该机构能够检测死锁的发生,并精确地确定与死锁有关的进程和资源。
  • 解除 是与检测相配套的一种措施,用于将进程从死锁状态下解脱出来

死锁的预防

只要破坏四个必要条件中的任何一个就能够预防死锁的发生。

  • 破坏第一个条件 互斥条件:使得资源是可以同时访问的,这是种简单的方法,磁盘就可以用这种方法管理,但是我们要知道,有很多资源 往往是不能同时访问的 ,所以这种做法在大多数的场合是行不通的。
  • 破坏第三个条件 非抢占:也就是说可以采用 剥夺式调度算法,但剥夺式调度方法目前一般仅适用于 主存资源和处理器资源 的分配,并不适用于所有的资源,会导致 资源利用率下降。

所以一般比较实用的 预防死锁的方法,是通过考虑破坏第二个条件和第四个条件。

  • 静态分配策略 静态分配策略可以破坏死锁产生的第二个条件(占有并等待)。所谓静态分配策略,就是指一个进程必须在执行前就申请到它所需要的全部资源,并且知道它所要的资源都得到满足之后才开始执行。进程要么占有所有的资源然后开始执行,要么不占有资源,不会出现占有一些资源等待一些资源的情况。 静态分配策略逻辑简单,实现也很容易,但这种策略 严重地降低了资源利用率,因为在每个进程所占有的资源中,有些资源是在比较靠后的执行时间里采用的,甚至有些资源是在额外的情况下才使用的,这样就可能造成一个进程占有了一些 几乎不用的资源而使其他需要该资源的进程产生等待 的情况。
  • 层次分配策略 层次分配策略破坏了产生死锁的第四个条件(循环等待)。在层次分配策略下,所有的资源被分成了多个层次,一个进程得到某一次的一个资源后,它只能再申请较高一层的资源;当一个进程要释放某层的一个资源时,必须先释放所占用的较高层的资源,按这种策略,是不可能出现循环等待链的,因为那样的话,就出现了已经申请了较高层的资源,反而去申请了较低层的资源,不符合层次分配策略。

死锁的代码实例

通过两个线程相互持有对方需要的锁资源,从而形成死锁。

java 复制代码
public class deadLock {
    private static final Object lock1 = new Object();
    private static final Object lock2 = new Object();

    public static void main(String[] args) {
        Thread thread1 = new Thread(() -> {
            synchronized (lock1){
                System.out.println("thread1 : holding lock1");
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("thread1 : getting lock2 ");
                synchronized (lock2){
                    System.out.println("thread1 : holding lock1 && lock2");
                }
            }
        });

        Thread thread2 = new Thread(() -> {
            synchronized (lock2){
                System.out.println("thread2 : holding lock2");
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                System.out.println("thread2 : getting lock1");
                synchronized (lock1){
                    System.out.println("thread2 : holding lock2 && lock1");
                }
            }
        });

        thread1.start();
        thread2.start();
    }
}

死锁链(多个锁串起来)

java 复制代码
public class DeadlockChain {
    private static final Object Lock1 = new Object();
    private static final Object Lock2 = new Object();
    private static final Object Lock3 = new Object();

    public static void main(String[] args) {
        Thread t1 = new Thread(() -> {
            synchronized (Lock1) {
                System.out.println("T1 locked Lock1");
                try { Thread.sleep(100); } catch (Exception e) {}
                synchronized (Lock2) {
                    System.out.println("T1 locked Lock2");
                }
            }
        });

        Thread t2 = new Thread(() -> {
            synchronized (Lock2) {
                System.out.println("T2 locked Lock2");
                try { Thread.sleep(100); } catch (Exception e) {}
                synchronized (Lock3) {
                    System.out.println("T2 locked Lock3");
                }
            }
        });

        Thread t3 = new Thread(() -> {
            synchronized (Lock3) {
                System.out.println("T3 locked Lock3");
                try { Thread.sleep(100); } catch (Exception e) {}
                synchronized (Lock1) {
                    System.out.println("T3 locked Lock1");
                }
            }
        });

        t1.start();
        t2.start();
        t3.start();
    }
}

内容参考:javaguide

相关推荐
Lee川1 天前
优雅进化的JavaScript:从ES6+新特性看现代前端开发范式
javascript·面试
Lee川1 天前
从异步迷雾到优雅流程:JavaScript异步编程与内存管理的现代化之旅
javascript·面试
晴殇i1 天前
揭秘JavaScript中那些“不冒泡”的DOM事件
前端·javascript·面试
绝无仅有1 天前
Redis过期删除与内存淘汰策略详解
后端·面试·架构
绝无仅有1 天前
Redis大Key问题排查与解决方案全解析
后端·面试·架构
AAA梅狸猫1 天前
Looper.loop() 循环机制
面试
AAA梅狸猫1 天前
Handler基本概念
面试
Wect1 天前
浏览器缓存机制
前端·面试·浏览器
掘金安东尼1 天前
Fun with TypeScript Generics:玩转 TS 泛型
前端·javascript·面试
掘金安东尼1 天前
Next.js 企业级落地
前端·javascript·面试