ReentrantLock 支持中断等待、多 Condition、可尝试获取锁及公平性可选,synchronized 则无中断响应、单等待队列、无非阻塞获取且默认非公平;使用 ReentrantLock 须严格配对 lock/unlock 并善用高级特性。

ReentrantLock 支持中断等待,synchronized 会卡死线程
当一个线程在等锁时被外部中断(比如用户取消任务、超时熔断),synchronized 完全无感——它会继续傻等,直到拿到锁或 JVM 终止。而 ReentrantLock 提供 lockInterruptibly(),让线程能及时响应 InterruptedException 并退出等待。
- 典型场景:RPC 调用超时、批处理任务主动终止、Web 请求 cancel 按钮
- 错误写法:
lock.lock()仍会忽略中断;必须用lock.lockInterruptibly() - 注意:调用该方法的代码必须声明
throws InterruptedException,且不能在catch里吞掉异常
ReentrantLock 可绑定多个 Condition,synchronized 只有 wait/notify 一套机制
synchronized 下所有 wait() 都挤在同一个队列里,notifyAll() 一喊全醒,再靠 while 循环过滤条件,效率低还容易误唤醒。ReentrantLock 允许通过 lock.newCondition() 创建多个独立的等待队列,比如「生产者等 notFull」和「消费者等 notEmpty」互不干扰。
- 真实需求:阻塞队列、读写分离、状态机驱动的协作(如“等初始化完成”“等配置加载完毕”)
- 关键点:
Condition.await()会自动释放锁,signal()不会立即抢锁,而是让对应等待线程参与下一轮竞争 - 别漏掉
signal()或signalAll(),否则线程永远沉睡
ReentrantLock 能尝试获取锁,synchronized 没有“非阻塞”选项
synchronized 是“要么拿到,要么挂起”,没有中间态。而 ReentrantLock 的 tryLock() 和 tryLock(long, TimeUnit) 让你可以控制是否等待、等多久、甚至放弃后干别的事。
- 适用场景:避免死锁(按顺序 tryLock)、短时临界区快速失败、后台健康检查轮询
- 常见坑:
tryLock()返回false时没做任何处理,导致逻辑跳过关键校验 - 注意:即使
tryLock()成功,也必须配对unlock(),否则锁泄漏
公平性可选,但默认非公平——别误以为 ReentrantLock 天然更“公平”
ReentrantLock 构造时传 true 才是公平锁(new ReentrantLock(true)),否则和 synchronized 一样是非公平的。公平锁看似合理,但实测吞吐量常下降 20%~50%,因为要维护 FIFO 队列+额外 CAS 开销。
立即学习“Java免费学习笔记(深入)”;
- 只在明确存在“线程饥饿”风险时启用公平模式,比如长耗时任务 + 大量短任务混跑
-
synchronized根本不提供公平开关,JVM 实现就是非公平的,这点两者一致 - 别为了“心理公平”滥用公平锁,高并发下它反而拖慢整体响应
真正难的不是选哪个锁,而是判断要不要锁——很多性能问题其实源于过度同步,或者把锁粒度搞得太粗。用 ReentrantLock 的高级功能前,先确认你是不是真需要它;而一旦用了,lock()/unlock() 必须成对出现在 try/finally 里,这是唯一不能妥协的底线。










