在runnable中用reentrantlock.trylock()避免死锁的核心是不阻塞、及时释放、按序尝试:立即返回成败结果,超时控制,失败后主动降级或放弃,并逆序释放已持锁,多线程严格按固定顺序(如hashcode)申请锁。

在 Runnable 中用 ReentrantLock.tryLock() 避免死锁,核心是**不阻塞、及时释放、按序尝试**——不是单纯加锁,而是控制获取失败时的行为。
用 tryLock() 替代 lock(),避免无限等待
tryLock() 会立即返回 true(成功获取)或 false(锁被占用),不会挂起线程。这从根源上切断了“互相等待”的链条。
- 直接调用
lock.tryLock():非公平模式下尝试一次,不等 - 用
lock.tryLock(timeout, unit):指定超时,比如tryLock(100, TimeUnit.MILLISECONDS),避免长时间空转 - 获取失败必须明确处理——不能忽略返回值,也不能直接重试(可能引发忙等)
获取失败时主动放弃或降级处理
死锁常源于“拿不到A就一直等A”,而 tryLock 的价值在于让你能优雅绕过。
- 跳过当前任务:比如日志写入失败,记录 warn 后继续执行后续逻辑
- 使用备选方案:如缓存未命中时,改用本地计算而非强等分布式锁
- 释放已持锁再退出:若已成功获取部分锁,需按**逆序**逐一
unlock(),防止资源泄漏
多锁场景下严格固定顺序
即使用了 tryLock,若两个线程以不同顺序申请锁(如线程1先A后B,线程2先B后A),仍可能因竞争导致活锁或逻辑异常。
- 约定所有线程按相同对象哈希码排序:比如
if (obj1.hashCode() - 或统一用枚举/ID定义全局锁顺序,避免依赖实例地址
- 只对真正需要同步的临界区加锁,缩小锁粒度比加锁顺序更重要
配合 finally 确保 unlock() 必然执行
tryLock() 成功后,必须在 finally 块中调用 unlock(),否则锁永远无法释放。
- 错误写法:
if (lock.tryLock()) { doWork(); lock.unlock(); }—— 异常时 unlock 不执行 - 正确写法:
if (lock.tryLock()) { try { doWork(); } finally { lock.unlock(); } } - 注意:只有
tryLock()返回true才能调用unlock(),否则抛IllegalMonitorStateException
不复杂但容易忽略:tryLock 是工具,关键在你怎么响应失败。锁不是目的,完成任务才是。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











