破坏死锁的关键是稳定破坏循环等待条件;统一锁获取顺序(如按哈希值或资源层级)、用tryLock+超时回退、避免混用synchronized与ReentrantLock,而互斥与不可剥夺不可强行消除。

打破 Java 中死锁的四个必要条件,关键不是逐个“理论消除”,而是聚焦可工程落地的控制点。其中只有 循环等待 条件能被代码逻辑稳定破坏;其余三个(互斥、持有并等待、不可剥夺)在 Java 锁机制下天然成立,强行绕开会牺牲正确性或性能,不现实。
优先破坏循环等待:统一锁获取顺序
这是最有效、最常用、也最可控的手段。只要所有线程以完全相同的全局顺序申请多个锁,环形依赖就不可能形成。
- 按对象哈希值排序:使用
System.identityHashCode(obj)比较锁对象,始终先 lock 小值对象,再 lock 大值对象 - 为资源定义明确层级:比如账户操作中,规定 always lock userAccount before lock bankAccount
- 避免动态决策:不在持有锁 A 后,根据业务分支决定下一步 lock B 还是 C;把顺序写死在主流程里
缓解持有并等待:用 tryLock + 超时 + 回退
虽然不能彻底取消“持有并等待”,但可以防止它演变成无限阻塞。
- 用
ReentrantLock.tryLock(long, TimeUnit)替代无条件lock(),设置合理超时(如 100–500ms) - 获取失败时,主动释放已持锁,并短暂休眠后重试,避免线程僵死
- 注意:synchronized 不支持超时,所以涉及多锁场景应优先选用 ReentrantLock
避免混用锁机制:保持可见性与一致性
看似无关,实则直接影响 JVM 对锁关系的识别能力。
立即学习“Java免费学习笔记(深入)”;
- 同一资源不要一部分用
synchronized,另一部分用ReentrantLock—— jstack 看不到跨机制的阻塞链 - 混合使用会让“循环等待”在工具层面隐形,排查时误判为普通卡顿
- 统一锁类型,确保线程栈 dump 能完整呈现等待关系
不推荐硬破互斥或不可剥夺
这两个条件是 Java 并发安全的基石。
- 取消互斥 → 数据竞争、脏读、状态错乱,比死锁更危险
- 强制剥夺锁 → JVM 不支持,且会破坏锁的语义契约,引发不可预测行为
- 银行家算法等预分配策略在 Java 锁模型中无对应实现,也不适配运行时动态加锁场景


















