Java的多线程如何避免死锁

7 篇文章 0 订阅

1.死锁和必要条件

多个线程同时在等待对方释放锁的时候,由于谁都没办法先释放锁,这个时候程序无法继续往下推进,就产生了死锁。
先列一下死锁的四个必要条件(由于是线程内容,所以以线程为单位):

  • 互斥使用:指某部分资源在被这个线程使用的时候,其他线程无法使用。
  • 不可剥夺:请求资源的线程不能强行从拥有资源的线程手上抢资源,只能等对方主动放弃资源。
  • 请求和保持:拥有资源的线程在不放弃原有资源的情况下又去申请新的资源。
  • 环路等待:存在一个等待队列{T1, T2, T3 … TN},其中T1请求T2的资源,T2又请求T3的资源…直到TN又请求T1的资源,形成一个环路等待队列。

2.避免死锁的办法

死锁要同时满足以上四个条件才能形成,而少了其中一个条件的话就无法形成死锁,所以,想要避免死锁,只要避免四个条件同时成立即可。
由于避免互斥使用没有意义,避免不可剥夺难以达成,就说明如何避免请求和保持以及避免环路等待。

2.1 避免请求和保持

  • 避免多次锁定:避免线程在已经拥有资源的时候,又去申请新的资源,如果要申请新的资源,就先放弃原有的资源。
  • 只锁定部分资源:如果实在需要锁定资源,那么只锁定极为必要一小部分(比如说一个类里面的一个字段),那么只锁定这一小部分,放弃锁定其他内容,再去申请新的资源。
  • 设定相同的加锁顺序:如果线程1先锁A再锁B,线程2先锁B再锁A,就容易形成死锁。如果改成线程1和2都是先锁A再锁B,就可以避免死锁了。

2.2 避免环路等待

  • 设定等待时间:在wait()的时候添加一个mills参数,让线程的等待时间有一个最大值,这样也可以避免死锁。
  • 设定定时锁:使用Lock对象加锁时,不使用lock()方法,而是使用tryLock()方法,这个方法可以添加一个mills参数,设定一个最大的锁定时间,超过时间就会自动解锁。

3. 实战排查死锁方式

那么怎么在实际做项目的时候考虑到死锁可能带来的影响呢?其实很简单

当你在对某个代码块加锁1的时候,你就要对这段代码的调用处和被调用处全部都看一遍,如果在其中有一处也有加锁2,那么这个时候就要注意了,我们要2锁住的所有调用处也过一遍,看看这一部分的代码块中有没有给加锁A的情况,如果有就表示有可能会产生死锁。

比如说某个方法的调用链 A -> B -> C -> D -> E -> F,我们现在要给方法C加锁1,那这个时候我们要把A-F的所有调用关系都看一遍,如果说在方法F,又存在一个锁2。那就要遍历一下锁2所有锁住的代码块,看看有没有调用中锁住锁1的情况。

参考材料

死锁 - 百度百科
https://baike.baidu.com/item/死锁/2196938#2_1
Java线程之如何分析死锁及避免死锁 - CSDN
https://blog.csdn.net/u010647035/article/details/79770534
疯狂JAVA讲义(第五版)
p757

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值