显式锁必须在finally块中释放以确保绝对回收,因try-catch无法覆盖return、break或catch中再抛异常等所有退出路径;标准写法是lock()在try外,unlock()在finally内,并用布尔标记避免非法unlock。

显式锁(如 ReentrantLock)不会自动释放,一旦在临界区抛出异常且未显式调用 unlock(),就会导致死锁或线程饥饿。最可靠的做法是在 try 块中加锁并执行业务逻辑,把 unlock() 放在 finally 块中——无论是否发生异常、是否提前 return,finally 都会执行,从而保证锁的绝对回收。
为什么不能只靠 try-catch?
仅用 try-catch 无法覆盖所有退出路径:
- catch 块里可能再次抛异常,导致后续 unlock 被跳过
- try 块末尾有 return 语句,会直接跳出,unlock 写在后面就永远不会执行
- 即使没异常,return 或 break 等控制流也会绕过 unlock
标准写法:lock → try → finally → unlock
正确结构如下:
lock.lock();
try {
// 业务逻辑:可能抛异常、可能 return
doSomething();
} finally {
lock.unlock(); // 100% 执行
}
注意:lock() 必须放在 try 外面——因为加锁本身也可能失败(如被中断),但那是获取锁阶段的问题,和“持有后未释放”是两个问题;我们聚焦的是“一旦拿到锁,就必须还”。
避免在 finally 中重复 unlock
如果锁是可重入的,且同一线程多次 lock(),必须确保 unlock() 次数匹配。但更常见风险是:lock() 失败(如被 interrupt() 中断)后,lock 对象仍处于未持有状态,此时在 finally 中调用 unlock() 会抛 IllegalMonitorStateException。
安全做法是引入布尔标记:
boolean locked = false;
try {
lock.lock();
locked = true;
doSomething();
} finally {
if (locked) {
lock.unlock();
}
}
替代方案:使用 try-with-resources(需封装)
Java 7+ 的 try-with-resources 要求资源实现 AutoCloseable。可自定义一个锁包装类:
public class AutoLock implements AutoCloseable {
private final Lock lock;
public AutoLock(Lock lock) {
this.lock = lock;
this.lock.lock();
}
@Override
public void close() {
lock.unlock();
}
}
使用时:
try (AutoLock ignored = new AutoLock(lock)) {
doSomething();
} // 自动 unlock
这种方式语义清晰,但增加了对象创建开销,且需额外维护封装类,适合锁使用频繁、团队规范统一的场景。
锁的生命周期管理本质是资源确定性释放问题。try-finally 不是银弹,却是最直接、零依赖、JVM 层级保障的方案。只要记住“lock 在外,unlock 进 finally”,就能避开绝大多数隐性死锁。










