死锁可从根源避免,关键在于事前打破四个必要条件之一:统一锁获取顺序(如按ID排序)、一次性申请全部资源并配合tryLock超时、减小锁粒度或使用无锁结构、采用可中断锁与超时机制。

Java 线程死锁的四个必要条件——互斥、请求与保持、不可剥夺、循环等待——必须**同时成立**才会发生死锁。因此,在设计阶段主动打破其中任意一个,就能从根源上避免死锁。关键不在于“事后检测”,而在于“事前约束”。
统一锁获取顺序,打破循环等待
这是最常用也最有效的设计手段。只要所有线程以相同的全局顺序申请多个锁,就不可能形成环形等待链。
- 例如:约定所有涉及 accountA 和 accountB 的转账操作,都先锁 ID 小的账户,再锁 ID 大的账户
- 可封装为工具方法:lockInOrder(account1, account2),内部按 hashCode 或业务主键排序后加锁
- 避免硬编码顺序(如“先锁 A 再锁 B”),否则不同模块可能约定冲突
一次性申请全部所需资源,消除请求与保持
线程在开始操作前就明确所需全部锁,并尝试一次性获取;若任一锁不可得,立即释放已获锁并退让重试,不持有部分资源等待其他资源。
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 使用 tryLock() + 超时机制(如 ReentrantLock.tryLock(500, TimeUnit.MILLISECONDS))
- 失败后释放已持锁,短暂休眠后重试,避免忙等
- 注意:synchronized 不支持超时尝试,必须改用显式 Lock
避免嵌套锁或限制锁粒度,弱化互斥与持有行为
虽然互斥本身难以消除(否则就不是并发控制了),但可通过设计减少锁竞争范围和深度:
立即学习“Java免费学习笔记(深入)”;
- 优先使用无锁结构(如 ConcurrentHashMap、AtomicInteger)替代同步块
- 将大锁拆分为多个细粒度锁(如按用户 ID 分段加锁),降低同时争抢概率
- 避免在持有锁期间调用外部方法(尤其是可能反向加锁的回调或服务)
使用可中断锁与超时机制,缓解不可剥夺的影响
不可剥夺是 Java 锁的固有特性,但可通过设计让线程“主动放弃”而非无限等待:
- 用 ReentrantLock.lockInterruptibly() 替代 synchronized,使线程能响应 interrupt 信号退出等待
- 所有阻塞操作(包括 wait、join、LockSupport.park)都应设置合理超时,防止永久挂起
- 配合监控告警:当某线程在锁上等待超过阈值(如 3 秒),记录堆栈并触发人工介入

















