首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

互斥不当的隐患

在回答上面的问题之前,先来想一个场景,早上小明骑车上班,聪明的小明想到一条可以抄近道去公司的胡同,所以他从胡同一端的入口进入,但和小明一样聪明的小刚也是这么想的,所以当小明骑到一半的时候,发现小刚从对面骑过来,但是由于胡同太过狭窄,只能容得下一辆车子行驶,于是暴脾气的两人......

如果把小明和小刚当成两个线程,而胡同两端的入口当成锁的话,没错,他们两个死锁了。因为想穿过这条胡同,必须要同时获取两端的入口这两把锁,而满脑子互斥思想的他们都只获取了一把,而且都不肯倒出胡同释放自己所持有的锁,都在等对方释放。最终,导致谁也过不去,新的线程也进不来,严重的话会耗尽系统资源。所以我们在日常开发过程中一定要注意这个问题,尽量避免死锁。

结合上面的例子,我们从发生死锁的条件入手,来分析一下如何避免死锁。条件归结起来有以下四点

互斥条件:资源在一个时间段只能被一个线程占有

占有且等待条件:线程A占有了资源X之后,在等待资源Y时,并不会释放X

不可抢占条件:当前线程所占有的资源没有被使用完时,不能被其他线程剥夺

环路等待条件:线程A等待线程B占有的资源,线程B等待线程A占有的资源,等待形成了一个相互依赖的闭环,永远没有进展。

可以看出,要发生死锁需要同时满足这四个条件,那么我们破坏其中任意一个不就可以解决问题了吗。

首先,互斥条件不能碰,因为还要靠它来解决原子性问题,所以我们只能从其他三个下手。

先来看“占有且等待条件”。要破坏这个条件,我们可以让线程一次性将X与Y全部申请了。比如胡同两端的入口都设一个监测器和显示屏,当有人进入时,胡同两端的屏幕都会显示---禁止入内,当人出去之后,两端的屏幕就变为---可以通行。这样只要从任何一端入口进入,都可一次性将两端入口都锁住。可参考以下代码

接下来从“不可抢占条件”看如何处理。这个的突破口其实很明显,线程持有资源不释放,导致其他线程不能获取,那么让陷入僵局的线程主动释放不就可以了吗。你可能会说,synchronized在申请资源时,如果没有申请到会进入阻塞状态,而进入阻塞状态的线程根本释放不了资源。没错,这个思路没有问题,synchronized是做不到,但别忘了我们上一篇还介绍了Lock,Lock也是通过锁来实现互斥的,而且它

1)可中断等待

2)支持超时方式获取锁

3)支持非阻塞方式获取锁

代码如下

当然,要解决具体问题,要结合具体业务和具体场景,上面只是为了说明通过以上三种方式可以破坏条件二。

最后来分析“环路等待条件”怎么破坏。这里的关键点在于环路,相互依赖,其实可以想到,如果线程获取资源的顺序都是一致的,便不会出现这种问题。比如,线程都先获取X资源,再获取Y资源。

通过互斥条件与上述三个破坏策略,我们解决了死锁问题。也知道虽然互斥思想帮我们解决了原子性问题,但实际的业务场景往往比较复杂,会有多把锁且锁之间需要相互配合的情况,这时很可能会产生死锁的问题,所以我们在使用互斥的时候,一定要考虑周全,互斥得当。

结语

其实,不管做什么,先考虑的一定不是怎么处理问题,而是怎么避免问题,正所谓“上医治未病,中医治欲病,下医治已病”。如果在设计上就可以避免加锁,也就不需要考虑死锁,如果本身就不需要多线程来处理,那就不要进行并发编程,有一句话虽然听着像玩笑,但我感觉说得很有道理---“处理并发问题最好的手段,那就是不写并发程序”。

- 完 -

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20200217A0NRP300?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券