java死锁是四个必要条件同时满足的确定性结果;互斥、持有并等待、不可剥夺、循环等待缺一不可,破坏任一即可避免死锁。

Java 中死锁不是随机发生的异常,而是四个条件**同时满足**时的确定性结果。排查要从这四个条件入手,破解则只需破坏其中任意一个——关键不在“全查”,而在“精准定位哪一环松动了就能破局”。
互斥条件:通常不破坏,但需确认是否被误用
这是并发安全的基础,比如 synchronized、ReentrantLock 默认就是互斥的。它本身不该被破坏,否则会引发数据竞争。但要注意:
- 检查是否对本可无锁访问的资源(如只读对象、线程局部变量)加了不必要的锁
- 确认锁粒度是否过大——例如整个方法用 synchronized,其实只需保护几行共享状态
- 避免在锁内做耗时操作(如 I/O、远程调用),这会放大持有时间,间接助长其他条件成立
持有并等待:最常出问题的环节,重点盯住锁获取顺序
线程拿着一把锁,又去申请另一把,且不释放手里的——这是死锁高频路径。排查时重点关注:
- 代码中是否存在嵌套 synchronized 块或多次 lock() 调用,且跨不同锁对象
- 是否在持有锁期间调用了可能触发新锁请求的外部方法(如 service 方法、回调、Stream 并行流)
- 使用 jstack 分析线程栈,看阻塞线程是否处于 “waiting to lock ” 状态,同时已持有其他 monitor
不可剥夺:Java 锁天然满足,但可通过设计绕过
synchronized 和默认 ReentrantLock 都不可被强制剥夺,所以不能指望系统回收。但你可以主动干预:
- 用 tryLock(timeout) 替代 lock(),超时失败就释放已持锁,再重试或降级处理
- 在持有锁期间避免调用可能长时间阻塞的方法,防止“锁占着不动”
- 对关键链路设计锁超时机制(如业务层面记录锁申请起始时间,超阈值主动放弃)
循环等待:最容易预防,靠统一锁序+工具检测
多个线程形成 A→B→C→A 的等待环,是死锁的显性标志。破解最直接:
- 为所有锁对象定义全局唯一顺序(如按 Class 名、对象 hashCode、或预设枚举),强制所有线程按此顺序加锁
- 用 jstack 或 JConsole 自带的“Detect Deadlock”按钮,一键识别循环等待链(JDK 自带,无需额外依赖)
- 在 CI 阶段引入 FindBugs / SpotBugs 插件,配置规则检测反向加锁模式(如先 lockB 后 lockA,而其他地方是先 A 后 B)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











