Java并发编程78讲--68 第68讲:发生死锁必须满足哪 4 个条件?

文章详细介绍了发生死锁的四个必要条件——互斥条件、请求与保持条件、不剥夺条件和循环等待条件,并通过一个Java线程同步示例进行了解析,说明如何形成死锁。此外,文章指出破坏任一条件可避免死锁,为解决死锁问题提供了思路。
摘要由CSDN通过智能技术生成

本课时我将为你介绍发生死锁必须满足哪 4 个条件。

发生死锁的 4 个必要条件

要想发生死锁有 4 个缺一不可的必要条件,我们一个个来看:

  • 第 1 个叫 互斥条件,它的意思是每个资源每次只能被一个线程(或进程,下同)使用,为什么资源不能同时被多个线程或进程使用呢?这是因为如果每个人都可以拿到想要的资源,那就不需要等待,所以是不可能发生死锁的。

  • 第 2 个是 请求与保持条件,它是指当一个线程因请求资源而阻塞时,则需对已获得的资源保持不放。如果在请求资源时阻塞了,并且会自动释放手中资源(例如锁)的话,那别人自然就能拿到我刚才释放的资源,也就不会形成死锁。

  • 第 3 个是 不剥夺条件,它是指线程已获得的资源,在未使用完之前,不会被强行剥夺。比如我们在上一课时中介绍的数据库的例子,它就有可能去强行剥夺某一个事务所持有的资源,这样就不会发生死锁了。所以要想发生死锁,必须满足不剥夺条件,也就是说当现在的线程获得了某一个资源后,别人就不能来剥夺这个资源,这才有可能形成死锁。

  • 第 4 个是 循环等待条件,只有若干线程之间形成一种头尾相接的循环等待资源关系时,才有可能形成死锁,比如在两个线程之间,这种“循环等待”就意味着它们互相持有对方所需的资源、互相等待;而在三个或更多线程中,则需要形成环路,例如依次请求下一个线程已持有的资源等。

案例解析

下面我们回到上一课时中所写的必然死锁的例子中,看看它是否一一满足了这 4 个条件,案例代码如下所示:

/**
 * 描述:     必定死锁的情况
 * */
 *public class MustDeadLock implements Runnable {
    public int flag;
    static Object o1 = new Object();
    static Object o2 = new Object();

    public void run() {
        System.out.println("线程"+Thread.currentThread().getName() + "的flag为" + flag);
        if (flag == 1) {
            synchronized (o1) {
                try {
                    Thread.sleep(500);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                synchronized (o2) {
                    System.out.println("线程1获得了两把锁");
                }
            }
        }
        if (flag == 2) {
            synchronized (o2) {
                try {
                    Thread.sleep(500);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                synchronized (o1) {
                    System.out.println("线程2获得了两把锁");
                }
            }
        }
    }

    public static void main(String[] argv) {
        MustDeadLock r1 = new MustDeadLock();
        MustDeadLock r2 = new MustDeadLock();
        r1.flag = 1;
        r2.flag = 2;
        Thread t1 = new Thread(r1, "t1");
        Thread t2 = new Thread(r2, "t2");
        t1.start();
        t2.start();
    }
 }

这个代码的具体分析和执行结果,我们在上一课时中已经介绍过了,这里不重复讲解,下面我们把重点放在对这 4 个必要条件的分析上。

我们先来看一下第 1 个互斥条件,很显然,我们使用的是 synchronized 互斥锁,它的锁对象 o1、o2 只能同时被一个线程所获得,所以是满足互斥条件的。

第 2 个是请求与保持条件,可以看到,同样是满足的。比如,线程 1 在获得 o1 这把锁之后想去尝试获取 o2 这把锁 ,这时它被阻塞了,但是它并不会自动去释放 o1 这把锁,而是对已获得的资源保持不放。

第 3 个是不剥夺条件,在我们这个代码程序中,JVM 并不会主动把某一个线程所持有的锁剥夺,所以也满足不剥夺条件。

第 4 个是循环等待条件,可以看到在我们的例子中,这两个线程都想获取对方已持有的资源,也就是说线程 1 持有 o1 去等待 o2,而线程 2 则是持有 o2 去等待 o1,这是一个环路,此时就形成了一个循环等待。

可以看出,在我们的例子中确实满足这 4 个必要条件,今后我们就可以从这 4 个发生死锁的必要条件出发,来解决死锁的问题,只要破坏任意一个条件就可以消除死锁,这也是我们后面要讲的解决死锁策略中重点要考虑的内容。

总结

以上就是本课时的内容,我们总结一下,在本课时主要介绍了要想发生死锁,必须满足的 4 个条件,分别是 互斥条件、请求与保持条件、不剥夺条件 和 循环等待条件;同时还分析了在上一课时中必然发生死锁的例子,可以看到,在这个例子中确实满足了这 4 个条件。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值