【javaEE初阶】多线程 _ 进阶篇 _ 死锁

☕导航小助手☕

      🍚写在前面

            🍜一、"死锁"出现的典型场景

            🍔二、产生 "死锁" 的必要条件

            🥡三、解决 "死锁" 问题的办法


写在前面

死锁,是多线程中的一个比较典型的问题,是多线程代码中的常见 bug ~

通俗的说,就是 在尝试加锁的时候,发现上次锁没有及时的释放(由于某些原因,或是是一些代码的 bug ),导致加锁没有加上 ~

于是,就会出现了 "死锁" 问题 ~

一、"死锁"出现的典型场景

(一)一个线程一把锁(可重入锁),若是不可重复锁,凉!

一个线程一把锁,这个就是之前所介绍的 可重入锁 ~

线程A 针对锁1 连续加锁两次,就构成了死锁 !

第一次加锁 加锁成功,第二次加锁 就需要第一次的锁释放,于是就阻塞等待;

但是,第一次加锁释放,就得依赖第二次加锁成功;

于是,"死锁" 就出现了!!!

(二)两个线程两把锁,相互获取对方锁,凉!

此时,不管是不是可重入锁,都会造成 "死锁" 问题 !

可以用一个例子来说明情况:

假设 甲同学 和 乙同学 居住在一起了 ~

甲同学 吃饺子喜欢蘸酱油,乙同学 吃饺子喜欢蘸醋,由于在一起了,生活习性都受到了彼此的影响,两位同学吃饺子的时候 喜欢都蘸上一点了 ~

有一天,吃饺子的时候,甲同学拿起了酱油,乙同学拿起了醋 ~

甲同学说:"你把醋给我,我用完了都给你";乙同学说:"你把酱油给我,我用完了就把醋给你" ~

此时,两者相持不下,这就造成了 "死锁" 问题 ~


此时,甲同学 和 乙同学 就可以看成是两个线程,酱油和醋 就可以看成是两把锁 ~

线程1 获取到锁A,线程2 获取到锁B;

线程1 尝试获取锁B(需要线程2 释放锁B) ,线程2 尝试获取到锁A(需要线程1 释放锁A);

在这种情况下,逻辑上就构成了循环,就构成了 "死锁" ~ 

(三)多个线程多个锁,"哲学家就餐问题",凉!

此时,这种情况和 第二种情况 类似,只是更复杂一点而已 ~

在多个线程多把锁的情况下,"死锁"问题 就是一个概率性的问题,但是也绝对不能忽视 !!!

在谈到 "多个线程多把锁" 的时候,就会引出一个很经典的问题 —— "哲学家就餐问题" !

故事背景:

有 5 个哲学家,相当于有 5 个线程,他们只会做两件事情:

  1. 思考人生(相当于是 线程休眠)
  2. 吃面条(相当于是 在CPU上运行)

由于多线程的调度是无序的,所以说 这几个哲学家 什么时候去思考人生,什么时候去吃面条,我们是不确定的 ~

同时,正常情况下,应该会有 5 双筷子~

但是,此时 在餐桌上 一共只有 5 根筷子,并且 这5 根筷子 分别在 两两哲学家 之间 ~

并且,他们之间都不相互嫌弃,吃面条的时候要拿起 左右手两双筷子(这就导致相邻的哲学家需要等待) ~

此处的筷子就视为 两把"锁",只有这两把锁都获取到了,才可以吃面条 ~ 

出现 "死锁" 问题的情况:

假设 在同一时刻,所有的哲学家都想吃面条 ~

他们同时伸出左手,拿起左边的筷子;然后又同时伸出右手,尝试去拿右边的筷子;此时,右手的筷子都拿不起来,因此都无法吃面 ~

由于哲学家都非常固执,导致即使吃不到面条 也不会放下左手的筷子,这样的情况就会一直持续下去 ~

于是,这就构成了死锁 ~

二、产生 "死锁" 的必要条件

鉴于 "死锁"问题,程序员大佬们 总结了 4 个 "死锁"产生的必要条件:

  1. 互斥使用(线程1 拿到锁A,其他线程就无法获取到 A)
  2. 不可抢占(线程1 拿到锁A,其他线程只能阻塞等待,等到线程1 主动释放锁,而不是强行把锁抢走)
  3. 请求和保持(当线程1 拿到锁A 之后,就会一直持有这个获取到锁的状态,直到说主动释放锁) 
  4. 循环等待(线程1 等待线程2,线程2 又尝试等待线程1)

三、解决 "死锁" 问题的办法

根据 产生 "死锁" 的必要条件,我们可以知道,前三个必要条件 都是在描述锁的基本特点,在实际情况下 我们并不好直接去破坏 ~

但是,第四个必要条件 却是和代码编写密切相关 ~

如果我们能够在编码上做出一些注意和约定,就可以打破 "循环等待",避免死锁 !!!

打破 "循环等待" 的办法:
针对多把锁,进行编号:1、2、3、4、......

并且约定在获取多把锁的时候,要明确获取锁的顺序是 从小到大(或者 从大到小) 的顺序 ~

如(此处以从小到大为例):

线程要拿到 1、2 这两把锁,就先获取 1,再获取 2;

线程要拿到 2、4 这两把锁,就先获取 2,再获取 4 ~

只要所有的线程都遵循这个顺序,就不会出现 "循环等待",就不会出现死锁 !!! 

我们可以把这个解决办法 带入到上面的 "哲学家就餐问题" 看看 ~

约定:获取所得顺序是:从小到大 ~

之后,最左边的哲学家 就可以得到两把锁了,于是就可以吃到面条了 ~

等到 吃完面条之后,会释放 4、5两把筷子;然后 最上面的哲学家 也可以吃到面条了,......,就这样的话,顺时针旋转,依次 5 位哲学家都可以吃上面条了 ~

谁最先吃到面条,谁最后吃到面条,一眼就可以看明白了 ~

于是,"循环等待"就被打破了,死锁问题就被解决了 ~

这种解决 "死锁"问题 的方法非常可靠,也非常的重要!!!

好了,关于 "死锁" 的知识点就介绍到这里了 ~

关于 多线程进阶篇的知识点 到这里也就完结撒花了 ~

如果感觉这一篇博客对你有帮助的话,可以一键三连走一波,非常非常感谢啦 ~

  • 28
    点赞
  • 15
    收藏
  • 打赏
    打赏
  • 26
    评论

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:撸撸猫 设计师:马嘣嘣 返回首页
评论 26

打赏作者

哎呀是小张啊

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值