黑马程序员技术交流社区

标题: 【上海校区】[核心技术36问]18.什么情况下java程序会产生死... [打印本页]

作者: 小影姐姐    时间: 2018-7-20 14:25
标题: 【上海校区】[核心技术36问]18.什么情况下java程序会产生死...
18.什么情况下java程序会产生死锁?如何定位、修复?

    死锁是一种特定的程序状态,在实体之间,由于循环依赖导致彼此一直处于等待之中,没有任何个体可以继续前进。死锁不仅仅是在线程之间会发生,存在资源独占的进程之间同样也可能出现死锁。通常来说,我们大多是聚焦在多线程场景中的死锁,指两个或多个线程之间,由于互相持有对方需要的锁,而永久处于阻塞的状态。

    定位死锁最常用的工具就是利用jstack等工具获取线程栈,然后定位相互之间的依赖关系,进而找到死锁。如果是比较明显的死锁,往往jstack工具就能直接定位,类似JConsole甚至可以在图形界面进行有限的死锁检测。

    如果程序运行时发生了死锁,绝大多数情况下都是无法在线解决的,只能重启、修正程序本身问题。所以,代码开发阶段相互审查,或者利用工具进行预防性排查,也是很重要的。


写一个基本的死锁程序:

    这个程序编译执行后,几乎每次都可以重现死锁。

为什么先调用t1.start(),但是t2却先打印出来了。因为线程调度依赖于操作系统调度器,虽然可以通过优先级之类的进行影响,但是具体情况是不确定的。

    下面模拟问题定位,jstack。

首先使用jps或者系统的ps命令、任务管理器等工具,确定进程ID:8508。

    如上图所示,找到处于BLOCKED状态的线程,按照试图获取(WAITING)的锁ID查找,很快就定位问题。jstack本身也会把类似的简单死锁抽取出来,直接打印出来。

    在实际应用中,类死锁情况未必有如此清晰的输出,但是总体上可以理解为:区分线程状态-查看等待目标-对比Monitor等持有状态。


如何在编程中尽量预防死锁?

    死锁的发生基本上是因为:

(1)互斥条件,类似java中Monitor都是独占的,要么是我用,要么是你用。

(2)互斥条件是长期持有的,在使用结束之前,自己不会释放,也不能被其他线程抢占。

(3)循环依赖关系。两个或多个个体之间出现了锁的链条环。

据此分析可能的避免死锁的思路和方法:

(1)尽量避免使用多个锁,并且只有需要时才持有锁;

(2)如果必须使用多个锁,尽量设计好锁的获取顺序;

(3)使用带超时的方法,为程序带来更多可控性。


有时候并不是阻塞导致的死锁,只是某个线程进入了死循环,导致其他线程一直等待,这种问题如何诊断?

    死锁的另一个好朋友就是饥饿。死锁和饥饿都是线程活跃性问题。实践中死锁可以使用JVM自带的工具进行排查。

    死循环死锁可以认为是自旋锁死锁的一种,其他线程因为等待不到具体的信号提示,导致线程一直饥饿。这种情况下可以查看线程CPU使用情况,排查出使用CPU时间片最高的线程,再打出该线程的堆栈信息,排查代码。

    基于互斥量的锁如果发生死锁往往CPU使用率较低,实践中也可以从这一方面进行排查。



作者: 不二晨    时间: 2018-7-20 17:52
优秀,奈斯
作者: 摩西摩西OvO    时间: 2018-7-23 13:32

作者: 摩西摩西OvO    时间: 2018-7-26 09:21

作者: 吴琼老师    时间: 2018-7-26 15:34





欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2