浅谈死锁(面试常问)

对于程序猿来说,死锁是一个非常致命且难以解决的问题,一旦一个程序出现死锁的情况,那么势必会带来非常严重的BUG。

什么是死锁?

死锁是指两个或两个以上的线程在执行过程中竞争同一资源且互不相让,最终导致整个系统陷入僵局的一种现象。

死锁的三种典型情况

  • 情况一:使用不可重入锁时,一个线程对同一对象进行两次加锁。(下文会展开讲述可重入与不可重入)

  • 情况二:两个线程(1和2)两把锁(A和B),在线程1获得锁A,线程2获得锁B后的同时,线程1又请求锁B且线程2又请求锁A的情况。
    例:这就好比有一个高处的柜子,里面有很多零食,熊大有钥匙,熊二有梯子,熊大想要熊二的梯子,熊二想要熊大的钥匙,两者互不相让,最终谁也吃不着的情况。

  • 情况三:多个线程多把锁
    例:经典的哲学家进餐例子。有五个哲学家,他们的生活方式是交替地进行思考和进餐。他们共用一张圆桌,分别坐在五张椅子上。在圆桌上有五个碗和五支筷子,平时一个哲学家进行思考,饥饿时便试图取用其左、右最靠近他的筷子,只有在他拿到两支筷子时才能进餐。进餐完毕,放下筷子又继续思考。
    在这里插入图片描述
    此时若刚好五个人都想吃东西,并都拿起了左手边的筷子,就会陷入死锁的情况。

可重入锁与不可重入锁

概念:允许同一进程多次获取同一把锁,是可重复可递归调用的锁,在外层使用锁之后,在内层仍然可以使用,并且不发生死锁,这样的锁就叫做可重入锁,不可重入锁反之。
例:线程A对一段代码块进行加锁后进入该代码块内部执行时,再次进行加锁操作,此时,倘若是可重入锁,便可以识别是同一线程,允许加锁,不会出现死锁现象;而如果是不可重入锁就不能进行第二次加锁,必须等待第一次加锁解开,而进行第一次加锁的就是这同一个线程,此时便进入了死锁状态。

死锁的四个必要条件

  1. 资源互斥:同一资源在同一时刻只能被一个线程/进程所用,其他线程请求该部分资源时,必须等待其释放。
  2. 不可抢占:进程获得的资源在未使用完时不可被抢占,只能在进程使用完时自己释放;
  3. 请求与保持:某进程已经保持了一个资源,但又请求另一个资源,若该资源被其他进程占有,此时请求阻塞,且对已经占有的资源不释放;
  4. 循环等待:发生死锁时,必然存在这样一个循环——进程p1等待p2占有的资源、进程p2等待p3占有的资源…进程pn等待p1占有的资源。

如何解决死锁问题

解决死锁问题的办法有很多,诸如资源一次性分配、银行家算法等,但是此类方法技术要求较高,且实现起来比较麻烦,因此本文给出的是一种较为简单高效的方法。
以上述哲学家进餐问题为例:
导致死锁的原因是因为在进餐时,每个哲学家都先取左手边的筷子,导致每个人手中都有且只有一根筷子,所以我们可以规定,在哲学家进餐时,不再固定先取左边的筷子,而是将所有筷子从大到小编号,如下图:
在这里插入图片描述
在进餐时,优先取左边和右边中的较小编号的筷子。在这种模式下,若五人同时想进餐,唐僧拿到一号筷子、沙僧二号、白龙马三号、猪八戒四号,孙悟空因为一号被拿走,所以进入阻塞等待,此时猪八戒便可以拿到五号筷子完成用餐,猪八戒用完白龙马也可以完成,依此类推所有人就都能完成用餐了。
用同样的办法可以解决大多数死锁问题,因为此方法打破了死锁的第四个必要条件——循环等待。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值