嵌套加锁不必然导致死锁,关键在于避免多线程以不同顺序交叉获取多把锁;应统一锁获取顺序、缩小同步范围、用tryLock+超时机制或改用高阶并发模型替代手动多锁管理。

嵌套加锁本身不必然导致死锁,真正危险的是**多个线程以不同顺序交叉获取多把锁**。只要控制好锁的申请逻辑和临界区边界,就能有效规避僵持。
统一锁获取顺序
这是最直接、最可靠的方式——让所有线程对同一组锁始终按相同顺序申请,从根源上切断循环等待链。
- 用 System.identityHashCode() 计算锁对象的唯一哈希值,按升序(或降序)依次获取
- 避免硬编码业务语义顺序(比如“先扣A账户再加B账户”),防止后续重构时顺序错乱
- 封装成工具方法,例如
lockInOrder(lockA, lockB, lockC),内部自动排序并加锁
缩小同步范围,禁止锁内调用外部逻辑
synchronized 块里调用可能隐式持锁的方法(如服务接口、toString()、回调、日志输出),极易引入不可见的第二把锁,形成嵌套依赖。
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
- 只包裹真正需要原子保护的代码,比如字段更新、状态校验等几行核心操作
- 提前读取所需数据,释放锁后再做网络请求、DB写入、格式化打印等耗时/外延操作
- 特别注意重写的 equals() 或 hashCode() 方法,若内部访问了其他被锁对象,也会埋下隐患
改用 tryLock + 超时机制
当锁顺序难以全局统一(如跨模块协作、动态资源ID),ReentrantLock 的 tryLock(long, TimeUnit) 提供主动控制权。
立即学习“Java免费学习笔记(深入)”;
- 按约定顺序尝试获取每把锁,任一失败立即 释放已成功获取的所有锁,避免“持有并等待”
- 配合指数退避重试,而非无脑 sleep 后重来;业务上也可考虑降级(如转异步补偿)
- 注意:synchronized 没有超时能力,必须显式切换为 ReentrantLock 才能启用该策略
优先用更高阶并发模型替代多锁
很多场景其实不需要手动管理多把锁,换种设计思路更安全、更简洁。
- 转账类操作:用 分段锁(如
locks[Math.abs(id % N)])代替全局两把账户锁 - 状态变更:用 AtomicReference.compareAndSet() 或 StampedLock 实现乐观更新
- 集合遍历/修改:直接选用 ConcurrentHashMap、CopyOnWriteArrayList,避免手写 synchronized + 迭代器

















