class="MsoNormal">死锁是一种特定的程序状态,在实体之间,由于循环依赖导致彼此一直处于等待之中,没有任何个体可以继续前进。死锁不仅仅是在线程之间会发生,存在资源独占的进程之间同样也可能出现死锁。通常来说,我们大多是聚焦在多线程场景中的死锁,指两个或多个线程之间,由于相互持有对方需要的锁,而永久处于阻塞的状态。
?
定位死锁最常见的方式就是利用jstack等工具获取线程栈,然后定位相互之间的依赖关系,进而找到死锁。如果是比较明显的死锁,往往jstack等就能直接定位,类似jconsole甚至可以在图形界面进行有限的死锁检测。
?
如果程序运行时发生了死锁,绝大多数情况下都是无法在线解决的,只能重启、修正程序本身的问题。所以,代码开发阶段玄乎审查,或者利用工具进行预防性排查,往往也是很重要的。
?
Java提供了标准的管理API-ThreadMXBean来进行死锁的定位跟踪,示例代码如下:
?
public static void main(String[] args) throws InterruptedException { ThreadMXBean mbean = ManagementFactory.getThreadMXBean(); Runnable dlCheck = new Runnable() { ??? @Override ??? public void run() { ??????? long[] threadIds = mbean.findDeadlockedThreads(); ??????? if (threadIds != null) { ????????????????????ThreadInfo[] threadInfos = mbean.getThreadInfo(threadIds); ????????????????????System.out.println("Detected deadlock threads:"); ??????????? for (ThreadInfo threadInfo : threadInfos) { ??????????????? System.out.println(threadInfo.getThreadName()); ??????????? } ?????????} ??????} ???}; ??????ScheduledExecutorService scheduler =Executors.newScheduledThreadPool(1); ??????// 稍等 5 秒,然后每 10 秒进行一次死锁扫描 ???????scheduler.scheduleAtFixedRate(dlCheck, 5L, 10L, TimeUnit.SECONDS); // 死锁样例代码… }
?
?需要注意的是,对线程进行快照本身是一个相对重量级的操作,需要慎重选择频度和时机。
?
如何在程序中尽量避免死锁?
1、如果可能,尽量避免使用多个锁,并且只有在需要时才持有锁,嵌套synchronized或lock很容易产生死锁。
2、如果必须使用多个锁,尽量设计好锁的获取顺序。
3、使用带超时的方法,为程序带来更多的可控性。