如何理解死锁?

目录

🐳今日良言:等风来,不如追风去

🐕一、死锁

🐇1.概念

🐇2.死锁的三个典型情况

🐇3.死锁的必要条件

🐇4.如何破除死锁


🐳今日良言:等风来,不如追风去

🐕一、死锁

🐇1.概念

多个线程在争夺资源时,陷入了僵持状态,都无法进行下去,都在等待对方释放资源,如果不干涉,将一直僵持下去

🐇2.死锁的三个典型情况

通过这三个典型情况,可以帮助理解死锁

1).一个线程一把锁,连续加锁两次,如果锁是不可重入锁就会死锁

2).两个线程两把锁,t1和t2先分别针对锁A和锁B进行加锁,再尝试获取对方的锁.

    t1先拿到锁A,然后获取锁B,t2先拿到锁B,然后获取锁A,此时就会发生死锁.

    举个例子:有一天小马和女朋友出去吃饺子,两人都喜欢用酱油和醋蘸着饺子吃,饺子上好以

    后,小马拿起了醋,女朋友拿起了酱油,然后女朋友对小马说:你把醋给我,我用完了就给你. 然

    后,小马说:你把酱油给我,我用完了给你.如果此时两个人互不相让,此时就发生死锁.

    针对这种情况,通过下面代码来加深理解:

public class ThreadDemo21 {
    public static void main(String[] args) {
        // 创建两个实例:jiangyou和cu
        Object jiangyou = new Object();
        Object cu = new Object();
        // 创建两个线程
        Thread t1 = new Thread(() -> {
            // 先拿到cu
            synchronized (cu) {
                try {
                    // 为了让t2拿到jiangyou
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (jiangyou) {
                    System.out.println("小马拿到了醋和酱油");
                }
            }
        });
        Thread t2 = new Thread(() -> {
            // 先拿到cu
            synchronized (jiangyou) {
                try {

                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (cu) {
                    System.out.println("女朋友拿到了醋和酱油");
                }
            }
        });
        t1.start();
        t2.start();
    }
}

此时运行代码,会发现程序无任何运行结果,说明没有线程拿到两把锁.

使用jconsole观察当前线程的情况

 如果打开jconsole没有进程的话,以管理员身份运行

3).多个线程多把锁

经典案例:哲学家就餐问题

问题描述:假设现在餐桌上有五个哲学家,每个哲学家的面前都放着一碗面条,桌子上有5根筷子,每个哲学家都需要两根筷子才可以吃面条,每个哲学家都有两种状态:1.思考人生(相当于线程的阻塞状态)   2.拿起筷子吃面条(相当于线程获取到锁,执行指令)    由于操作系统的随机调度,这5个哲学家随时都可能想吃面条或者思考人生,要想吃面条必须拿起左右手的两根筷子,情况图如下:

假设在某一时刻,发生极端情况,此时就会发生死锁

例如:同一时刻,所有哲学家都拿起左手的筷子,此时哲学家们都拿不到右手的筷子,都需要等待右边的哲学家将筷子放下.

🐇3.死锁的必要条件

死锁有四个必要条件:

1).互斥使用

    线程1拿到了锁,线程2就只能阻塞等待,无法使用锁.

2).不可抢占

    线程1拿到锁后,线程2不能强夺锁,只能阻塞等待,等待线程1释放锁

3).请求和保持

    线程1拿到锁A后,再尝试获取锁B,此时锁A还是保持的(并不会因为要获取锁B就释放锁A)

4).循环等待

    线程1尝试获取锁A和锁B   线程2尝试获取锁B和锁A

    线程1在获取锁B的时候等待线程2释放锁B,线程2在获取锁A的时候等待线程1释放锁A

🐇4.如何破除死锁

在死锁的四个必要条件中,前三个都是锁的基本特性,我们无法更改,所以说破除死锁的突破口就是:循环等待

如何破除循环等待呢?

最简单的办法:给锁编号,然后指定一个固定的顺序(比如从小到大)来加锁,任意线程加多把锁的时候,都让线程遵守上述顺序,此时循环等待自然破除.

以哲学家就餐问题为例:破除循环等待,为每一个筷子编号,从小到大,然后从第一个哲学家开始,规定:每次先拿编号小的筷子,然后拿编号大的筷子.

此时,对于哲学家5来说,无法拿起筷子,因为编号1的筷子已经被哲学家1拿走了,此时哲学家5阻塞等待,此时,对于哲学家4来说,他就可以拿起编号为5的筷子,然后吃面条,吃完后,放下筷子,此时哲学家3可以吃面条,以此类推,最后死锁就会被破除了.

针对上面的两个线程两把锁的代码,破除循环等待的代码如下.

public class ThreadDemo21 {
    public static void main(String[] args) {
        // 规定:jiangyou 是1号  cu 是2号  约定:先拿小的,再拿大的
        Object jiangyou = new Object();
        Object cu = new Object();
        // 创建两个线程
        Thread t1 = new Thread(() -> {
            // 先拿到cu
            synchronized (jiangyou) {
                try {
                    // 为了让t2拿到jiangyou
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (cu) {
                    System.out.println("小马拿到了醋和酱油");
                }
            }
        });
        Thread t2 = new Thread(() -> {
            // 先拿到cu
            synchronized (jiangyou) {
                try {

                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (cu) {
                    System.out.println("女朋友拿到了醋和酱油");
                }
            }
        });
        t1.start();
        t2.start();
    }
}

 此时运行代码:

 死锁的问题就得到了解决.

还有一种办法破除死锁的办法,银行家算法,但是过于复杂,这里只介绍简单的解决死锁的办法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值