线程死锁
来源:互联网 发布:java数据结构与算法 编辑:程序博客网 时间:2024/06/02 19:28
线程死锁
安全性和活跃度通常是相互制约的,虽然可以通过开辟一定量的线程来提高活跃度,但是用来保证多线程安全的锁也可能引起锁顺序死锁问题(lock-ordering deadlock)。类似,我们使用线程池和信号量来约束资源的使用,但是也可能存在资源死锁(resource deadlock)。Java程序不能从死锁中恢复,所以能够避免死锁对于程序的设计十分重要。
死锁
哲学家就餐是多线程中很经典的问题,它就存在死锁风险。五位哲学家都抓住右边的叉子,同时等待左边的叉子而不放弃右边的叉子时,那么任意一位哲学家都吃不到面,从而饿死。在Executor任务执行中提交了子任务,同时当前线程池没有多余的线程可供使用,而母线程等待子线程的结果,子线程等待母线程的计算资源时,也会出现死锁问题。此外,当线程A占有对象锁L时,想要获得另一个对象锁M,线程B持有对象锁M,想要获得对象锁L,两个线程将永远等待下去,这种彼此等待的死锁称为“依赖死锁”。
当一个线程永远占有另一个线程所等待的锁,而另一个线程尝试去获得这个锁,那么它们将永远被阻塞。
- 锁顺序死锁
哲学家就餐问题中,每位哲学家在尝试获取叉子时,如果顺序不一致(不是按照一个方向,而是每位哲学家任意挑选左右两边的一只筷子),那么就可能导致锁顺序死锁问题,如下图所示。
验证锁顺序的一致性需要对程序中锁的行为进行分析,单独观察每一个锁的代码路径并不能得出锁顺序死锁的结论。
public class Test { public static void main(String[] args) { Left left = new Left(); Right right = new Right(); left.setRight(right); right.setLeft(left); left.start(); right.start(); } public static class Left extends Thread{ private Right right; public void setRight(Right right) { this.right = right; } public void synchronizedLeft() { synchronized(this) { try { Thread.sleep(10); //如果把try...catch放到synchronized里面呢? synchronized(right) { right.sayMyName(); } } catch(Exception e) { //操作 } } } public void sayMyName() { System.out.println("my name is Left"); } @Override public void run() { this.synchronizedLeft(); } } public static class Right extends Thread{ private Left left; public void setLeft(Left left) { this.left = left; } public void synchronizedRight() { synchronized(this) { try { Thread.sleep(10); //如果把try...catch放到synchronized里面呢? synchronized(left) { left.sayMyName(); } } catch(Exception e) { //操作 } } } public void sayMyName() { System.out.println("my name is Riht"); } @Override public void run() { this.synchronizedRight(); } } }
从上面的图片和代码中可以看出,锁顺序死锁造成原因是,线程A先锁定Left,在尝试获取Right锁时,Right锁已经被B线程锁定了,而B线程又在等待A线程释放Left锁。所以,如果线程A先锁定了Left,又在B之前锁定了Right,那么锁顺序死锁就不会发生。问题关键,是如何保证获取锁时,另一个线程不会占用资源。
银行进行转账时,两个账户间会不会有死锁问题存在?这也是死锁比较经典的场景。这种常见也比较明显,就是在同一个方法中获取两个锁。
- 协作对象间的死锁
锁顺序死锁在代码分析上比较容易察觉,因为锁的获取口径比较直观,即synchronized嵌套了另一个synchronized。协作对象间的死锁,则不易察觉,因为对象间的方法调用带来了synchronized的嵌套。锁顺序是一种显示的锁定,而协作对象间的锁是隐士的锁顺序。public class Test { public static void main(String[] args) { final Left left = new Left(); final Right right = new Right(); left.setRight(right); right.setLeft(left); left.start(); right.start(); } public static class Left extends Thread{ private Right right; public void setRight(final Right right) { this.right = right; } /** * 从方法调用上,很难一眼看出synchronized嵌套 */ public synchronized void sayMyName1() { try { Thread.sleep(10); /* * 这里是对象间协作时发生了死锁,因为Left获取了锁后,在right中有尝试获取锁, * 也就是隐士synchronized嵌套,这种称为对象协作间死锁 */ this.right.sayMyName1(); } catch(Exception e) { //操作 } } public synchronized void sayMyName2() { System.out.println("you name is Right"); } public void run() { this.sayMyName1(); } } public static class Right extends Thread{ private Left left; public void setLeft(final Left left) { this.left = left; } public synchronized void sayMyName1() { System.out.println("you name is Left"); } /** * 从方法调用上,很难一眼看出synchronized嵌套 */ public synchronized void sayMyName2() { try { Thread.sleep(10); /* * 这里是对象间协作时发生了死锁,因为Right获取了锁后,在left中有尝试获取锁, * 也就是隐士synchronized嵌套,这种称为对象协作间死锁 */ this.left.sayMyName2(); } catch(Exception e) { //操作 } } public void run() { this.sayMyName2(); } }}
在持有锁的时候调用外部方法要小心死锁问题,因为外部方法可能会获取其他的锁,或者发生阻塞,当一个线程持有锁的时候会阻塞其他尝试获取该锁的线程。
阅读全文
0 0
- 线程死锁
- 线程死锁
- 线程死锁
- 线程死锁
- 线程死锁
- 线程死锁
- 线程死锁
- 线程死锁
- 线程死锁
- 线程-死锁
- 线程死锁
- 线程死锁
- 线程死锁
- 线程死锁
- 线程死锁
- 线程死锁
- 线程死锁
- 线程死锁
- Anaconda环境管理
- AtCoder:Colorful Balls(思维 & 数论)
- JAVA 操作hadoop分布式文件系统
- Activity源码分析
- LeetCode Algorithms 55. Jump Game
- 线程死锁
- : “m_RichEdit”: 未知重写说明符解决办法
- Problem A: 递归基础之放苹果
- 读书计划
- mysql的搭建
- php+js+mysql+ajax实现简单留言板功能,新手教程
- lesson6.8与日期和事件有关的类
- JS事件冒泡代码
- java的keyAdapter总结