死锁的成因及其解决办法

🔒一.什么是死锁?

死锁就是两个或者两个以上的线程在执行过程中,由于资源竞争或者由于彼此通信而造成的阻塞现象,若无外力作用,都将无法推进下去.

二.关于死锁的情况🤷‍♂️

情况☝️:一个线程 一把锁,如果是可重入锁不会产生死锁,不可重入锁会产生死锁.
我们该怎么去理解这种情况呢???
下面的这个代码可以帮助大家理解这种情况:

public static void main(String[] args) {
        Object locker=new Object();
        Thread t1=new Thread(()->{
            synchronized (locker){
                synchronized (locker){
                    
                }
            }
        });
    }

这样的情况就容易造成死锁,同一个线程对同一个对象锁;
外层首先获得锁对象,然后进去代码也尝试对这个对象加锁,它要加锁成功就必须等外层锁释放,但是外层释放锁又必须等里面代码执行完.就这样两个锁就僵持在这里,就产生了死锁.


情况✌️:两个线程两把锁,对于这种情况,即使是可重入锁也是会造成死锁的.

首先我们给大家画个图让大家理解一下这是个什么情况
线程死锁
为了避免大家写出这样的代码,我给大家写一个两个线程两把锁产生死锁的代码.

public static void main(String[] args) {
        Object locker1=new Object();
        Object locker2=new Object();
        Thread t1=new Thread(()->{
            synchronized (locker1){
                System.out.println("线程t1,获取锁1");
            }
            synchronized ((locker2)){
                System.out.println("线程t1,又尝试获取锁2");
            }
        });
        t1.start();
        Thread t2=new Thread(()->{
           synchronized (locker2){
               System.out.println("线程t2,获取锁2");
           }
           synchronized (locker1){
               System.out.println("线程t2,又尝试获取锁1");
           }
        });
        t2.start();
    }

这就是典型的在自己没有释放锁的情况又尝试去获取对方的锁.
如果大家对上面的两个例子还不够理解的话,我们举一个生活中的例子来帮助大家来理解.
eg:大家在生活中肯定都吃过饺子,吃饺子肯定是要蘸料的.比如我是一个喜欢吃辣又喜欢吃酸的妹子,我有一个朋友呢,她的口味和我就很像,她也是又吃酸又吃辣的;有一次呢,我们一起去吃饺子,她先拿到了醋,而我就先拿辣椒;事情的重点就来了,我就让她把手中的醋给我,然后她让我把手中的辣椒给她.我们同时还占着自己手中的蘸料不放.这样我们就僵持在这里,谁都释放不了,谁都加不了.这就一种死锁.我和我的朋友就是两个线程,手中的辣椒和醋就是两把锁


情况👌:N个线程M把锁[哲学家问题]

哲学家问题:
假设有五位哲学家围坐在一张圆形餐桌旁,做以下两件事情之一:吃饭,或者思考。吃东西的时候,他们就停止思考,思考的时候也停止吃东西。餐桌中间有一大碗意大利面,每两个哲学家之间有一只餐叉。因为用一只餐叉很难吃到意大利面,所以假设哲学家必须用两只餐叉吃东西。他们只能使用自己左右手边的那两只餐叉.然后哲学家们也不善于交谈,假如他们同时都拿起了左手边的叉子,然后都在等待右手边的叉子,哲学家们都很固执,没吃到面都不会放下手中的叉子.然后这就产生了死锁,都僵持在这里

这里哲学家就是一个个的线程,叉子就相当于锁.

三.死锁的必要条件🔒[同时具备]
  • 互斥使用:一个线程拿到一把锁之后,另一个线程不能使用(基本特点)
  • 不可抢占:一个线程拿到锁,只能自己主动释放,不能是被其他线程强行占用
  • 请求和保持:意思就是尝试获取另外的锁时,自己的锁是不释放的.典型的"吃着碗里的看着锅里的"
  • 循环等待:类似于上面的哲学家问题,一号哲学家等着二号手中的释放,二号等三号的…这样进行循环等待.
四.如何解决死锁问题

破坏死锁的任意一个或者多个条件就可以解决死锁问题啦,其中被修改的条件只能是后面的两个:请求与保持和循环等待.因为基本特点是改变不了的.

1.修改请求与保持:当我们线程获得一把锁之后不能再去请求获取另一把锁

2.破坏循环等待:我们可以为锁都编上号,约定都只能先对小编号的加锁,再才能对编号大的加锁
还是上面哲学家问题:
我们可以规定都拿起手边最小编号的筷子:(就得到以下的图)
线程然后我们再让拿起手边大编号的筷子,这样只有5号滑稽老铁可以拿到编号5的筷子,其他老铁就需要阻塞等待,这样5号滑稽老铁就可以吃上面条了.吃完之后他就可以释放锁了(放下筷子),这样4号滑稽老铁就可以就可以拿起编号4的筷子,它就可以吃上面条了…以此类推,这样所有的滑稽老铁都吃上面条了.

代码可以这样实现:

 public static void main(String[] args) {
        Object locker1 = new Object();
        Object locker2 = new Object();
        Thread t1 = new Thread(() -> {
           synchronized (locker1){
               synchronized (locker2){
                   
               }
           }
        });
        t1.start();
        Thread t2 = new Thread(() -> {
           synchronized (locker1){
               synchronized (locker2){
                   
               }
           }
        });
        t2.start();
    }

这样就不会产生死锁了,当t1线程对locker1加锁了,t2线程也想对locker1加锁时,
t2线程就需要阻塞等待,等t1线程执行完t2才能够获取锁.

_OK,以上就是我对死锁的理解,如有错误请大家指出!_😊😊😊

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值