synchronized不预防死锁,关键在设计源头切断死锁条件:统一加锁顺序(如按identityhashcode排序)、缩小同步范围、避免锁内io或嵌套加锁、改用reentrantlock超时机制,并通过jstack或threadmxbean主动检测。

synchronized 本身不预防死锁,它只是提供互斥语义;能否避免死锁,完全取决于你如何组织加锁逻辑。关键不是等死锁发生再去处理,而是从设计源头切断形成条件。
统一加锁顺序,破坏循环等待
这是最有效、最常用的预防手段。只要所有线程以完全一致的顺序获取多个锁,就不会出现 A 等 B、B 等 A 的闭环。
- 按对象身份哈希值排序:用 System.identityHashCode() 比较锁对象,小的先锁
- 业务约定优先级:比如“用户锁必须在订单锁之前获取”,所有模块严格遵守
- 封装加锁逻辑:把多锁获取封装成工具方法,避免各处手写顺序不一致
缩小同步范围,减少持有时间
锁持有越久,其他线程等待越久,死锁概率越高。synchronized 块应只包裹真正需要保护的临界区代码。
- 避免在 synchronized 块内做 IO、远程调用、sleep 或复杂计算
- 不要在锁内调用可能再次加锁的外部方法(如另一个服务的同步方法)
- 能用局部变量或不可变对象替代共享状态的,就尽量不用锁
改用 ReentrantLock 配合超时机制
当必须处理多个锁且顺序难以绝对统一时,ReentrantLock 提供了更可控的应对方式。
- 用 tryLock(long, TimeUnit) 设置合理超时(如 100–300ms),失败则释放已持锁并重试或降级
- 务必在 finally 块中 unlock(),防止锁泄漏
- 对外部中断敏感的场景,优先使用 lockInterruptibly() 而非 lock()
主动检测与快速定位
预防不能 100%杜绝问题,线上环境需具备快速发现能力。
- 本地测试可用 jstack
,搜索 “Found 1 deadlock” 直接定位线程和锁链 - 运维环境可集成 ThreadMXBean.findDeadlockedThreads() 到健康检查接口,自动告警
- JConsole 图形界面点“检测死锁”按钮,直观展示等待关系图
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











