Java并发编程实战 04死锁了怎么办? (2)

只要同时获取到转出账户和转入账户的资源锁。执行完转账操作后,也同时释放转入账户和转出账户的资源锁。那么则不会出现死锁。但是使用synchronized只能同时锁定一个资源锁,所以需要建立一个锁分配器LockAllocator 。代码如下:

/** 锁分配器(单例类) */ public class LockAllocator { private final List<Object> lock = new ArrayList<Object>(); /** 同时申请锁资源 */ public synchronized boolean lock(Object object1, Object object2) { if (lock.contains(object1) || lock.contains(object2)) { return false; } lock.add(object1); lock.add(object2); return true; } /** 同时释放资源锁 */ public synchronized void unlock(Object object1, Object object2) { lock.remove(object1); lock.remove(object2); } } public class Account { // 余额 private Long money; // 锁分配器 private LockAllocator lockAllocator; public void transfer(Account target, Long money) { try { // 循环获取锁,直到获取成功 while (!lockAllocator.lock(this, target)) { } synchronized (this){ synchronized (target){ this.money -= money; if (this.money < 0) { // throw exception } target.money += money; } } } finally { // 释放锁 lockAllocator.unlock(this, target); } } }

使用while循环不断的去获取锁,一直到获取成功,当然你也可以设置获取失败后休眠xx毫秒后获取,或者其他优化的方式。释放锁必须使用try-finally的方式来释放锁。避免释放锁失败。

3.尝试获取锁资源

在Java中,Lock接口定义了一组抽象的加锁操作。与内置锁synchronized不同,使用内置锁时,只要没有获取到锁,就会死等下去,而显示锁Lock提供了一种无条件的、可轮询的、定时的以及可中断的锁获取操作,所有加锁和解锁操作都是显示的(内置锁synchronized的加锁和解锁操作都是隐示的),这篇文章就不展开来讲显示锁Lock了(当然感兴趣的朋友可以先百度一下)。

总结

在生产环境发生死锁可是一个很严重的问题,虽说重启应用来解决死锁,但是毕竟是生产环境,代价很大,而且重启应用后还是可能会发生死锁,所以在编写并发程序时需要非常严谨的避免死锁的发生。避免死锁的方案应该还有更多,鄙人不才,暂知这些方案。若有其它方案可以留言告知。非常感谢你的阅读,谢谢。

参考文章:
《Java并发编程实战》第10章
极客时间:Java并发编程实战 05:一不小心死锁了,怎么办?
极客时间:Java核心技术面试精讲 18:什么情况下Java程序会产生死锁?如何定位、修复?

个人博客网址: https://colablog.cn/

如果我的文章帮助到您,可以关注我的微信公众号,第一时间分享文章给您

微信公众号

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwgxfx.html