
前言
Deadlock撰写了另一篇文章,因为这是一个必须引起注意的严重问题. 这并不是夸大死锁的风险. 尽管锁通常会保留很短的时间,但作为商业产品的应用程序可能每天执行数十亿次锁获取->释放操作,只要这些数十亿次操作期间发生错误java多线程避免死锁,就可能导致程序中出现死锁,即使它通过了压力测试,也无法找到所有潜在的死锁.
死锁
一个经典的多线程问题.
当一个线程永久持有一个锁而其他线程试图获取该锁时,众所周知,它们将永远被阻塞. 如果线程A持有锁L并希望获取锁M,而线程B持有锁M并希望获取锁L,则这两个线程将永远等待. 这是最简单的死锁形式.
在系统的设计中,考虑了死锁的监视和从死锁中的恢复. 如果检测到一组事务具有死锁,它将选择一个受害者并放弃该事务. Java虚拟机在解决死锁问题方面不如强大. 当一组Java线程死锁时,这两个线程将永远无法再使用,并且由于这两个线程持有两个锁,因此这两个同步的代码/代码块将不再运行,除非终止应用程序并重新启动
死锁是一个设计错误,问题更加模糊. 但是,死锁的影响很少立即出现. 一个类可能是死锁的,这并不意味着每次都会发生死锁. 这只是意味着有可能. 当发生死锁时,它通常处于最坏的情况下,即高负载.

这是一个生成死锁并演示如何分析这是死锁的简单代码:
public class DeadLock { private final Object left = new Object(); private final Object right = new Object(); public void leftRight() throws Exception { synchronized (left) { Thread.sleep(2000); synchronized (right) { System.out.println("leftRight end!"); } } } public void rightLeft() throws Exception { synchronized (right) { Thread.sleep(2000); synchronized (left) { System.out.println("rightLeft end!"); } } } }
请注意,必须有“ Thread.sleep(2000)”使线程进入睡眠状态,否则一个线程正在运行,而另一个线程尚未运行. 首先运行的线程很可能已经连续获得了两个锁. 编写两个线程以分别调用它们:
public class Thread0 extends Thread { private DeadLock dl; public Thread0(DeadLock dl) { this.dl = dl; } public void run() { try { dl.leftRight(); } catch (Exception e) { e.printStackTrace(); } } }
public class Thread1 extends Thread { private DeadLock dl; public Thread1(DeadLock dl) { this.dl = dl; } public void run() { try { dl.rightLeft(); } catch (Exception e) { e.printStackTrace(); } } }
编写一个主函数调用:
public static void main(String[] args) { DeadLock dl = new DeadLock(); Thread0 t0 = new Thread0(dl); Thread1 t1 = new Thread1(dl); t0.start(); t1.start(); while(true); }

关于结果,没有结果,因为死锁,所以不会打印任何语句. 下面演示了如何定位死锁问题:
1,jps获取当前Java虚拟机进程的pid

2. jstack打印堆栈. 在jstack所打印内容的末尾,我们实际上已经报告发现了死锁,但是由于我们正在分析死锁的原因,而不是直接得出此处存在死锁的结论,因此,请别理它,看看在上一部分

首先解释每个部分的含义,以“ Thread-1”为例:
(1)“线程1”表示线程名称

(2)“ prio = 6”表示线程优先级
(3)“ tid = 00000000497cec00”表示线程ID
(4)nid = 0x219c
与线程相对应的本地线程ID. 下面说明了这一关键点. 因为Java线程由附加到Java虚拟机的本地线程运行,所以本地线程实际上是在执行Java线程代码,只有本地线程才是真正的线程实体. 在Java代码中创建一个线程,虚拟机将在运行时创建一个相应的本地线程,并且该本地线程是真实的线程实体. 在Linux环境中,可以使用“ top -H -p JVM进程ID”来查看JVM进程下本地线程(也称为LWP)的信息. 请注意,此本地线程用十进制表示java多线程避免死锁,而nid用十六进制表示. ,只需对其进行转换,对应于0x219c的本地线程ID应该为8604.
(5)“ [0x000000004a3bf000..0x000000004a3bf790]”表示线程占用的内存地址
(6)“ java.lang.Thread.State: BLOCKED”指示线程的状态
在解释了每个部分的含义之后,可以看到Thread-1处于BLOCKED状态,而Thread-0处于BLOCKED状态. 分析这两个线程:

(1)线程1已获得锁0x000000003416a4e8,并正在等待锁0x000000003416a4d8
(2)线程0获得了锁0x000000003416a4d8,并正在等待锁0x000000003416a4e8
由于两个线程都在等待获取对方持有的锁,所以它们永远都在等待.
3. 注意使用Eclipse / MyEclipse. 如果不单击控制台上的红色框以终止它,而是右键单击->运行方式-> 1 Java应用程序,则此过程将始终存在. 此时,您可以使用taskkill命令终止尚未终止的进程:

如何避免死锁
由于可能存在死锁,让我们来谈谈如何避免死锁.
1. 让程序一次最多获得一个锁. 当然,在多线程环境中,这种情况通常是不现实的
2. 设计时请清楚考虑锁的顺序,并尝试减少嵌入式锁交互的次数
3. 由于死锁是由两个线程无限期地等待对方持有的锁引起的,因此只要存在等待时间的上限即可. 当然,同步没有此功能,但是我们可以在Lock类中使用tryLock方法来尝试获取锁. 此方法可以指定一个超时时间,并在返回此时间限制后返回失败消息
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-173485-1.html
做好“法律战”准备
慢慢慢
美国现役主战舰艇