并发编程——死锁问题

博客介绍了细粒度锁可提高并行度,但可能导致死锁。死锁是线程相互等待致“永久”阻塞,发生需四个条件同时满足。解决死锁最好是规避,可通过破坏“占用且等待”“不可抢占”“循环等待”条件来实现,实际操作中需选成本低的条件破坏。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

synchronized 互斥锁 中,对于 如何用一个锁 来保护 多个有关联的资源中,我们用 Account.class 作为锁,解决转账问题。是的A B账户没有并发问题。虽然这个方案解决了并发问题,但是 所有的账户 的转账操作都变成了串行的。例如 账户A转入B,账户C转入 D,在这个方案中只能串行执行,性能太差,而现实世界中这两个操作是可以并行的。

所以我们可以采用细粒度锁,用不同的锁来保护不同的资源。对于这个转账操作来说,就是使用两把锁,一个锁定转出账户this,一个锁定转入账户 target。只有两个锁都加锁成功,则进行转账操作。这种情况下,账户A转入B,账户C转入 D的两个操作就可以并行了。代码如下:

使用细粒度锁,可以提高并行度,是性能优化的重要手段,而这个方式也有代价,那就是可能会导致死锁

对于以上代码情景:如果现在有两个线程,分别执行账户A给B转100元,账户B给A转一百。那么两个线程会在第一步给this加锁,也就是线程A给账户A加锁,线程B给账户B加上了锁,然后准备去获取target锁的时候,线程A发现账户B已经被加锁,所以账户A只能等待,同时持有账户B的锁的线程B也在等待账户A的锁,因为两个线程都只能等待下去,无法进行。这也就是我们说的死锁

在发生死锁的时候,一般没有什么特别好的办法,只能重启应用。因而,解决死锁最好的办法就是规避死锁


死锁:一组相互竞争资源你的线程相互等待,导致“永久”阻塞的现象。

发生死锁的四个条件 (必须一下四个条件同时发生才会死锁):
互斥:共享资源X 和 Y 同一时刻只能被一个线程占用。
占有且等待:线程1 已经取得了 共享资源X,在等待 共享资源Y 的时候,不释放 共享资源X。
不可抢占:其他线程不能强行抢占线程1占有的资源
循环等待:线程1 等待 线程2 占有的资源,线程2 等待 线程1占有的资源,就是循环等待。

对于以上四个条件,我们只要破坏一个就可以避免死锁。
互斥是不可以破坏的,因为锁就是互斥的。
对于“占用且等待”,我们可以一次性申请所有资源,这样就不用等待了。
对于“不可抢占”,占用部分的资源的线程,在进一步申请其他资源的时候,申请不到就释放自己战友的资源。
对于“循环等待”,可以按序申请资源,也就是给资源排个序,先申请前边的在申请后边的。就不会循环了。


1、破坏占用且等待资源的办法 (一次性申请所有的资源)

如一下代码,定义一个Allocator类,用于同时申请和同时释放我们所需要的资源。

但是上面代码中的while循环,如果在apply()耗时很长或者并发量大的情况下是很消耗CPU的。解决办法

while(!actr.apply(this, target))

2、破坏不可抢占条件
但是synchronized是做不到了,因为当synchronized申请资源申请不到的时候,下城就进入了阻塞状态啥也做不了。也就释放不了资源了。java语言层级解决不了这个问题,但是SDK层面还是解决了的。

3、破坏循环等待条件
给所需申请的资源排序,按序申请。这里需要保护的资源是账户,所以我们给账户增加一个属性id,申请时候按照从小到大申请

在现实操作中,我们需要根据操作成本,去选择一个成本较低的条件进行破坏


参考文章

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值