Java死锁需主动干预:①超时重试(tryLock+释放已持锁);②统一加锁顺序(如按ID升序);③ThreadMXBean检测后中断线程并清理锁;④缩小临界区,避免同步块内调用外部加锁方法。

Java 中多线程相互等待资源形成死锁后,JVM 本身不会自动解除(除非使用带超时的显式锁),必须通过设计干预或运行时手段主动打破。关键不在于“唤醒”某个线程,而在于**中断等待链、释放已持资源、或避免循环依赖固化**。
主动释放 + 超时重试(最实用)
用 ReentrantLock.tryLock(long, TimeUnit) 替代无条件 lock(),给每次加锁设定合理超时。一旦抢锁失败,立即释放已持有的所有锁,稍后重试。
- 避免“拿了A不放,死等B”的僵持状态
- 需配合业务逻辑做幂等处理(重试不重复扣款等)
- 示例:转账操作中,若无法在500ms内同时拿到两个账户锁,则释放已持锁并退避100ms再试
统一加锁顺序(从根源预防)
死锁本质是获取顺序混乱。只要所有线程对同一组资源按**全局一致规则排序加锁**,循环等待就不可能出现。
- 比如按对象 ID 升序:先锁
Math.min(acc1.getId(), acc2.getId())对应的账户,再锁另一个 - 适用于 synchronized 和 ReentrantLock,且无需额外同步开销
- 注意:ID 必须稳定、不可变,且排序逻辑在线程间完全一致
利用 ThreadMXBean 检测 + 主动中断
程序可定期调用 ThreadMXBean.findDeadlockedThreads() 检测死锁。发现后,选择性中断其中一个死锁线程(需确保该线程能响应中断并正确释放锁)。
立即学习“Java免费学习笔记(深入)”;
- 中断前检查线程是否处于
WAITING或BLOCKED状态 - 被中断线程应在
catch(InterruptedException)中清理已持锁(如 unlock()) - 慎用:仅适合可控场景(如后台任务),不建议用于核心支付等强一致性流程
避免嵌套调用与外部方法
同步块内调用未知第三方方法,等于把锁控制权交出去——对方可能反过来抢你没防备的另一把锁。
- 将临界区尽量缩小,只包裹真正共享数据的操作
- 同步块内不调用可能加锁的 service 方法、RPC、数据库操作等
- 必要时拆分为“先校验→释放锁→再执行→最后更新”三阶段


















