死锁和死锁代码实现

死锁概念

什么是死锁

死锁(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

相关推荐
绝无仅有13 小时前
Go 并发同步原语:sync.Mutex、sync.RWMutex 和 sync.Once
后端·面试·github
绝无仅有13 小时前
Go Vendor 和 Go Modules:管理和扩展依赖的最佳实践
后端·面试·github
国家不保护废物14 小时前
10万条数据插入页面:从性能优化到虚拟列表的终极方案
前端·面试·性能优化
lovebugs15 小时前
Kubernetes 实战:Java 应用配置与多环境管理
后端·面试·kubernetes
大米饭消灭者16 小时前
markdown-it是怎么将markdown转为html的
前端·面试
轻语呢喃18 小时前
async/await:从语法糖到并发优化的异步编程
前端·面试
你我约定有三18 小时前
面试tips--JVM(2)--对象创建的过程
jvm·面试·职场和发展
默默地离开18 小时前
一篇文章彻底搞懂跨域问题
前端·javascript·面试
你我约定有三20 小时前
面试tips--JVM(3)--类加载过程
jvm·面试·职场和发展