直接回答:死锁需同时满足四个条件:互斥、占有且等待、不可剥夺、循环等待,破坏任意一个即可避免。排查首选 jstack 打线程栈,它能直接报告发现的死锁。

排查手段

  1. jps 找到进程后 jstack <pid>,输出中会明确提示 Found one Java-level deadlock 并列出互相持有的锁;
  2. 图形化工具:JConsole、VisualVM 的"检测死锁"按钮,或 Arthas 的 thread -b 找出阻塞其他线程最多的线程;
  3. 线上可持续监控线程状态与锁等待指标,结合日志定位。

避免策略

  • 固定加锁顺序:所有线程按统一全局顺序获取多把锁(如按对象 hash 或业务 ID 排序),破坏循环等待;
  • 超时放弃:用 ReentrantLock.tryLock(timeout) 替代无期限等待,失败后释放已有锁重试;
  • 缩小锁粒度与持有时间:锁内不做 IO、不调用未知的外部方法(开放调用原则);
  • 优先使用并发工具类ConcurrentHashMap、阻塞队列等,减少手写加锁;
  • 引入锁超时告警、统一用工具方法管理多锁申请。

易错点:死锁一旦发生 JVM 无法自动解除,只能中断/重启相关线程,因此重在预防而非恢复。追问:ThreadMXBean.findDeadlockedThreads() 编程式检测、哲学家就餐问题的解法。