什么是死锁?

文章介绍了死锁的概念,通过火锅蘸料和哲学家就餐的比喻解释了死锁的产生,详细阐述了死锁的三个典型情况,包括单线程加锁两次、两个线程两把锁以及哲学家就餐问题。此外,解释了可重入锁的概念,并列出了死锁的四个必要条件:互斥使用、不可抢占、请求和保持、循环等待。最后,提出了通过给锁编号并指定加锁顺序来避免循环等待,从而防止死锁的方法。
摘要由CSDN通过智能技术生成

目录

死锁是怎么回事?

死锁的三个典型情况

可重入和不可重入

死锁的四个必要条件

如何破除死锁


死锁是怎么回事?

有一天我和小明一起火锅,小明点的麻酱味的蘸料,我点的麻辣味的蘸料,我想尝尝小明麻酱味的,小明想尝尝麻辣味的,但是如果我两互不相让,那么就会产生死锁。

死锁的三个典型情况

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

比如synichronized,ReentrantLock都是可重入锁,他们连续加锁两次不会出现死锁

2.两个线程两把锁,t1和t2各自先针对锁A和锁B加锁,再尝试获取对方的锁,就好比吃火锅,就会产生相互阻塞,两个线程都无法获取到对方的锁


public class ThreadDemo {
    public static void main(String[] args) {
        Object locker1 = new Object();
        Object locker2 = new Object();
        Thread t1 = new Thread(()->{
            synchronized (locker1){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (locker2){
                    System.out.println("t1获取到locker2");
                }
            }
        });
        Thread t2 =  new Thread(()->{
            //t2获取到locker2
            synchronized (locker2){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (locker1){
                     System.out.println("t2获取到locker1");
                }
            }
        });
        t1.start();
        t2.start();
    }
}

3.多个线程多把锁:哲学家就餐问题:

 

有5位哲学家围坐在一张圆桌旁,桌子中央放了一碗面,,每两个人之间放了一把叉子,每位哲学家思考人生饥饿了这就相当于线程的阻塞状态,然后开始吃面,这相当于线程获取到锁,哲学家必须获得两把叉子,且每个人只能直接从紧邻自己的左边或右边去取叉子,但是由于操作系统随机调度,这五位哲学家可能会随时饥饿了吃面条,也可能随时要思考人生

假设同一时间 五个哲学家同时要开始吃面,那么他们会同时拿起左叉子,所有的哲学家都拿不起右手的筷子,都在等待右边的哲学家把筷子放下,但是互不忍让,这样所有的人都拿不了有筷子,也就所有的线程都互相阻塞。

可重入和不可重入

一个线程针对一把锁,连续加锁两次,是否会死锁,不会死锁就是可重入的,synchronized就是可重入锁

死锁的四个必要条件

1.互斥使用:线程1拿到锁,线程2就得等着

2.不可抢占:线程1拿到锁之后,必须是线程1主动释放锁,不能说是线程2就把锁给强行获取到

3.请求和保持 :线程1拿到锁A之后,再尝试获取锁B,A这把锁还会保持,不会因为获取锁B就把A释放了

4.循环等待:线程1尝试获取锁A和锁B,线程2尝试获取锁B和锁A,线程1在获取B的时候等待线程2释放B,同时线程2在获取A的时候等待线程1释放A

如何破除死锁

在死锁的四个必要条件中来看,前三个条件是synchronized锁的基本特性,那么我们只需要打破必要条件即可,就是第四个条件循环等待,就好比打仗的时候,要找到敌军的突破口去打更容易赢

办法:给锁编号,然后指定一个固定的顺序,比如从小到大来加锁,任意线程加多把锁的时候,都让线程按照顺序来加锁,那么循环等待自然破除。比如哲学家就餐问题

 

我们给叉子(锁)编号 规定哲学家先拿自己左右手两边编号小的,这样的话会发现最上面的哲学家左右手两边最小的是1,但是1已经被别人拿走了,那么这时候他就会阻塞等待,这时候他左边的哲学家就可以拿起两把叉子去吃面了,他吃完以后就放下④号和⑤号筷子,这时候他左边的哲学家就可以开始拿着③号和④号筷子吃饭,就这么下去,所有的哲学家都会吃到面。而吃火锅蘸料问题,我们也可以给蘸料编号,我和小明都先吃编号小的,再吃编号大的蘸料,这样问题就迎刃而解了。

public class ThreadDemo {
    public static void main(String[] args) {
        Object locker1 = new Object();
        Object locker2 = new Object();
        Thread t1 = new Thread(()->{
            synchronized (locker2){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (locker1){
          System.out.println("t1获取到locker2");
                }
            }
        });
        Thread t2 =  new Thread(()->{
            //t2获取到locker2
            synchronized (locker2){
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (locker1){
                     System.out.println("t2获取到locker1");
                }
            }
        });
        t1.start();
        t2.start();
    }
}

这样t1和t2都可以获取到对方的锁,就不会产生死锁了

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值