Java死锁

1. 什么是死锁

  • 死锁,简单来说就是多个线程被同时阻塞,且每个线程都在等待其他线程释放资源,从而导致所有线程都无法继续执行的状态。
  • 如下图所示,线程 A 持有资源 2,线程 B 持有资源 1,他们同时都想申请对方的资源,所以这两个线程就会互相等待而进入死锁状态。
    死锁示意图

2. 死锁的产生条件

  • 死锁产生的四个必要条件:
    • 互斥条件:资源不能被多个线程共享,某个资源一次只能被一个线程占用。
    • 请求与保持条件:一个线程因请求资源而阻塞时,对已获得的资源保持不放。
    • 不可剥夺条件:线程已获得的资源在未使用完之前,不能被强制剥夺,只能在使用完后由线程自己释放。
    • 循环等待条件:存在一种资源循环等待关系。

3. 如何检测死锁

  • 使用jmapjstack等命令查看JVM线程栈和堆内存的情况。如果有死锁,jstack的输出中通常会有Found one Java-level deadlock:的字样,后面会跟着死锁相关的线程信息。另外,实际项目中还可以搭配使用topdffree等命令查看操作系统的基本情况,出现死锁可能会导致CPU、内存等资源消耗过高。
  • 采用VisualVM、JConsole等工具进行排查。

4. 如何预防和避免死锁

  • 预防死锁,破坏死锁产生的必要条件即可:
    • 破坏请求与保持条件:一次性申请所有的资源
    • 破坏不可剥夺条件:当线程请求资源时,如果资源不可用,则释放它已获得的资源,稍后再试
    • 破坏循环等待条件:靠按序申请资源来避免。按某一顺序申请资源,释放资源则反序释放
  • 避免死锁:在资源分配时借助算法对资源分配进行计算评估,如银行家算法