ReentrantLock 是 synchronized 的补充而非替代,仅在需中断等待、超时获取、公平调度或多个 Condition 时使用;必须配 try-finally 确保 unlock(),且 Condition 使用须持锁调用。

ReentrantLock 不是 synchronized 的替代品,而是补充——它在需要中断等待、超时获取、公平调度或绑定多个条件时才真正有用。
什么时候该用 ReentrantLock 而不是 synchronized
多数场景下,synchronized 更简单安全;只有明确需要以下能力时,才考虑 ReentrantLock:
- 线程在等待锁时可被
interrupt()中断(synchronized遇到中断会继续阻塞) - 尝试获取锁但不想无限等待,比如用
tryLock(1, TimeUnit.SECONDS)设置超时 - 需要按 FIFO 顺序调度等待线程(启用公平模式:
new ReentrantLock(true)) - 一个锁对应多个
Condition实例(如生产者/消费者各自 await 不同条件)
lock() / unlock() 必须成对出现在 try-finally 中
忘记在 finally 块中调用 unlock() 是最常见且致命的错误,会导致锁永远无法释放,后续线程全部阻塞。
ReentrantLock lock = new ReentrantLock();
lock.lock();
try {
// 临界区操作
doSomething();
} finally {
lock.unlock(); // 这一行绝不能少,也不能放在 try 或 catch 里
}
注意:lock.lockInterruptibly() 可被中断,但同样要配 finally;而 tryLock() 成功后也必须 unlock(),失败则无需 unlock。
立即学习“Java免费学习笔记(深入)”;
Condition 的使用和陷阱
ReentrantLock 自带 newCondition() 方法,返回的 Condition 比 Object.wait()/notify() 更灵活,但规则更严格:
- 必须由持有该锁的线程调用
await(),否则抛IllegalMonitorStateException -
signal()不会释放锁,只唤醒等待线程;被唤醒线程仍需重新竞争锁 - 一个
Condition实例只能用于一个锁,不可跨锁复用 - 避免用
signalAll()在高并发下引发“惊群效应”,优先考虑精准signal()
ReentrantLock lock = new ReentrantLock();
Condition notFull = lock.newCondition();
Condition notEmpty = lock.newCondition();
// 生产者
lock.lock();
try {
while (isFull()) {
notFull.await(); // 等待非满
}
addToBuffer(item);
notEmpty.signal(); // 唤醒一个消费者
} finally {
lock.unlock();
}
公平性开关不是性能优化项,而是语义选择
构造时传 true 启用公平模式:new ReentrantLock(true),意味着锁按等待时间顺序分配。但这会显著降低吞吐量——因为每次都要遍历同步队列。
- 默认非公平模式(
false或无参构造)允许插队:刚释放锁的线程可能立刻再次抢到,减少上下文切换 - 公平模式仅在业务逻辑**强依赖执行顺序**时才启用,例如任务调度器、资源配额系统
- 不要为了“看起来更合理”而开启公平模式,它不解决死锁,也不保证实时性
锁的重入、可监控(getHoldCount()、isLocked())、可中断这些特性,都是以额外字段和 CAS 开销换来的——用不用,得看真实需求是否压倒这点成本。










