ReentrantLock 不自动避免死锁,需通过统一锁序、tryLock超时回退、finally释放、慎用lockInterruptibly等手段打破循环等待和持有并等待。

ReentrantLock 本身不自动避免死锁,能否稳定运行取决于你如何设计加锁逻辑。关键不是锁的类型,而是打破死锁四条件中的“循环等待”和“持有并等待”。下面这些做法经过大量高并发系统验证,能切实降低死锁风险。
统一锁获取顺序
多个锁共存时,所有线程必须按相同规则排序后依次获取,彻底消除 AB/BA 依赖链。
- 按对象唯一标识排序(如账户 ID、资源编号),始终先锁小值再锁大值
- 避免在不同方法中用不同顺序调用 lockA.lock() 和 lockB.lock()
- 把锁顺序逻辑封装成工具方法,比如 getOrderedLocks(from, to) 返回按 ID 升序排列的锁数组
用 tryLock 配合超时与回退
不强行等待,主动放弃并清理,从根源上防止“拿了 A 等 B、又卡住不动”的状态。
- 调用 tryLock(200, TimeUnit.MILLISECONDS) 而非无参 tryLock(),给合理响应窗口
- 任一锁获取失败,立即释放已成功获取的锁(注意判断是否真的 acquired)
- 失败后不忙重试,可降级处理(如读缓存)、记录日志或返回默认值
确保 unlock 永远在 finally 中执行
忘记释放锁不会报错,但会导致其他线程永久阻塞——这是最隐蔽也最常见的“伪死锁”。
立即学习“Java免费学习笔记(深入)”;
- lock() 必须写在 try 块外,unlock() 必须写在 finally 块内
- 不要放在 catch 里:异常时可能根本没拿到锁,unlock() 会抛 IllegalMonitorStateException
- 即使方法中有 return 或 throw,finally 仍会执行,这是唯一安全位置
慎用 lockInterruptibly,明确中断语义
它不能预防死锁,但能让等待中的线程及时响应中断,避免无限挂起影响监控和运维。
- 仅在业务允许被中断的场景使用(如后台任务、可取消操作)
- 捕获 InterruptedException 后,务必恢复中断状态(Thread.currentThread().interrupt())
- 不要用它替代 tryLock —— 中断是协作机制,不是超时控制手段


















