Java并发之死锁
来源:互联网 发布:java的后缀名 编辑:程序博客网 时间:2024/04/19 17:12
简单聊一下死锁。
当一个线程永远的持有一个锁,并且其他线程都尝试获得这个锁,就会发生死锁。
如果发生死锁,通常JVM没有办法自动恢复,只能重启。对线上系统来说,这是灾难性的。
死锁的发生场景、例子
1,最简单的例子:
线程A持有锁1并尝试获得锁2,线程B占有锁2并尝试获得锁1。两个线程将会永远的等待下去。
2,多个线程,持有锁的同时,尝试获得对方的锁,形成一个复杂的环状依赖。这些线程都将无限的等待。
3,两个线程以不同的顺序获得相同的锁
举个栗子:
一段转账的代码,从A账号转钱到B账号,为了保持数据一致性,操作前需要对着两个账号加锁,避免账号被同步更改而产生错误的数据。
public void transfer(Account fromAccount,Account toAccount){ synchronized(fromAccount){ synchronized(toAccount){ //转账动作. } }}
上面这段代码,是什么情况下会死锁呢?
这样一个场景:
A线程:
transfer(account1,account2)
B线程:
transfer(account2,account1)
A获得account1的锁,等待account2的锁; B获得account2的锁,等待account1的锁。 两个线程无限的等待。
这就是“两个线程以不同的顺序获得相同的锁”。
死锁产生的条件
死锁的发生必须满足4个条件:
1,资源是不能共享的,同一资源不能被不同的线程占有
2,任务持有一个资源的同时,等待别的任务的资源
3,资源不能抢占,不能强行从其他线程抢占
4,产生循环等待
要发生死锁,上面的条件缺一不可。
如何避免死锁
上面说到了死锁的发生必须要满足四个条件。
所以避免死锁,我们可以从这四点着手,打破其中一条即可。
在多线程下,可以从几个方面:
1,加锁顺序:对于同样的资源,其加锁顺序一样。
比如上面的转账例子,
transfer(account1,account2)transfer(account2,account1)
这两个调用,不管参数的顺序如何,在加锁时,保证account1,account2是同样的顺序,就一定不会有死锁。
具体实现,可以设计一个哈希算法,来保证加锁的顺序。代码如下,这样对于同样的账号,不管是在哪个参数传进来,其hash值大小固定,加锁顺序就一定是一样的。 问题完美解决。
public void transfer(Account fromAccount,Account toAccount){ int fromHash = hash(fromAccount); int toHash = hash(toAccount); if(fromHash>toHash){ synchronized(fromAccount){ synchronized(toAccount){ //转账动作. } } }else{ synchronized(toAccount){ synchronized(fromAccount){ //转账动作. } } }}
2,加锁时间
使用Lock对象来加锁, 在尝试获取锁的时候,加上时间参数,超过时间就放弃操作,避免永久等待。
- Java并发之死锁
- java并发之死锁
- Java 并发编程之死锁
- Java多线程与并发(三)之死锁
- 【java并发】线程技术之死锁问题
- java编程思想笔记-并发之死锁
- Java并发死锁
- 【Java并发编程】之九:死锁(含代码)
- 【Java并发编程】之九:死锁(含代码)
- Java 并发编程之图形界面应用程序及死锁问题
- 【Java并发编程】之九:死锁(含代码)
- 【Java并发编程】之九:死锁(含代码)
- 【Java并发编程】之九:死锁(含代码)(r)
- 【Java并发编程】之九:死锁(含代码)
- Java并发编程之十一:死锁(含代码)
- 并发编程之避免死锁
- Java线程:并发协作-死锁
- Java线程:并发协作-死锁
- 【技巧】Eclipse ctrl+左键 进入方法
- 光标函数(自己整理的,求指导)
- ubuntu下修改键位
- python中subprocess.Popen执行命令并持续获取返回值
- 《Java编程技巧1001条》358条:控制随机整数的范围
- Java并发之死锁
- jsp中js,css文件相对地址的引用
- hadoop hdfs命令
- gradle 学习记录
- qt UI文件转py文件
- 谈一谈情报威胁与态势感知
- 16. PHP APC
- python 下面两段程序的区别,求解答
- servlet详解