ReentrantLock的核心优势是“可控”而非“更快”,支持lockInterruptibly()响应中断、多Condition精准唤醒、tryLock()避免死锁,但需严格配对lock/unlock且公平锁性能差。

ReentrantLock 的核心优势不是“更快”,而是“可控”——它把锁的生命周期、等待策略、唤醒逻辑,全交到你手上。
什么时候必须用 lockInterruptibly()?
当线程在等锁时,你希望它能被外部中断(比如服务优雅停机、超时熔断),synchronized 完全做不到。JVM 会忽略中断信号,线程卡在 monitorenter 就是死等。
- 典型场景:RPC 调用中,下游响应慢,主线程需在 5 秒内放弃等待锁并返回降级结果
- 错误写法:
synchronized块里调用Thread.interrupt()→ 无反应 - 正确写法:
public void doWork() throws InterruptedException { lock.lockInterruptibly(); // 这里会响应中断 try { // 临界区 } finally { lock.unlock(); } }
为什么需要多个 Condition 而不是只靠 wait()/notifyAll()?
一个 synchronized 块只能配一套 wait/notify,所有等待线程挤在一个队列里。而 ReentrantLock 可以按业务语义分组唤醒,避免“惊群效应”。
- 生产者-消费者模型中:
notFull.await()和notEmpty.await()是两个独立等待队列 - 调用
notFull.signal()只唤醒正在等“有空间”的生产者,不会误唤醒消费者 -
synchronized下只能用notifyAll(),每次都要遍历全部等待线程做条件重判,浪费 CPU
tryLock(long, TimeUnit) 解决什么真实问题?
它不是为了“抢锁快”,而是为了打破死锁僵局或实现非阻塞协作。
立即学习“Java免费学习笔记(深入)”;
- 常见错误现象:两个线程分别持有 A 锁和 B 锁,又同时尝试获取对方锁 → 死锁
- 用
tryLock可主动退让:if (lockA.tryLock(100, TimeUnit.MILLISECONDS)) { try { if (lockB.tryLock(100, TimeUnit.MILLISECONDS)) { try { // 同时拿到两把锁 } finally { lockB.unlock(); } } } finally { lockA.unlock(); } } - 注意:
tryLock失败后必须处理失败路径,不能直接重试(否则变忙等)
公平锁参数 new ReentrantLock(true) 别乱开
公平锁看起来“更合理”,但代价是性能暴跌——所有线程必须排队进 CLH 队列,连自旋机会都没有。
- 实测数据(JDK 17,4 核):高竞争下公平锁吞吐量比默认非公平锁低 3–5 倍
- 仅在极少数场景适用:你明确观察到某类线程长期饥饿(比如日志线程总抢不到锁),且能接受吞吐下降
-
synchronized没有公平选项,它天然是非公平的,这也是它优化空间更大的原因之一
真正容易被忽略的点:用 ReentrantLock 时,lock() 和 unlock() 必须严格成对,且 unlock() 一定要写在 finally 里——哪怕只漏一次,整个系统就可能卡死。而 synchronized 的“自动释放”不是便利性优势,是安全底线。










