ReentrantLock需手动unlock且必须用try/finally确保释放,synchronized由JVM自动释放;前者支持中断等待、公平锁和多个Condition,后者不支持。

ReentrantLock 必须手动 unlock,synchronized 不用管释放
这是最直接、最容易出问题的区别。用 synchronized 时,JVM 在方法退出或异常抛出后自动释放锁;而 ReentrantLock 必须显式调用 unlock(),否则锁永远不释放,其他线程会一直阻塞——这不是“可能出错”,是“一定会卡死”。
- 正确写法必须套
try/finally:先lock.lock(),再在finally块里调用lock.unlock() - 如果写成
if判断后才lock(),但忘了在所有分支里都unlock(),就埋下死锁隐患 - 不能在
catch中提前 return 却跳过unlock()—— 只有finally是安全的落点
等待锁时能否被中断?synchronized 不行,ReentrantLock 可以
当线程 A 持有锁很久(比如 IO 卡住、逻辑阻塞),线程 B 在等锁,这时如果想让 B 主动放弃等待去做别的事(比如响应超时、关闭请求),synchronized 完全做不到;而 ReentrantLock 提供了 lockInterruptibly()。
-
lockInterruptibly()被中断时会抛InterruptedException,你可以捕获并清理资源、返回错误或切换策略 -
synchronized下线程一旦进入阻塞等待状态,只能等锁释放,无法被外部打断 - 这个能力在定时任务、RPC 超时控制、服务优雅停机等场景中非常关键
公平性可选 vs 固定非公平:ReentrantLock 能按排队顺序给锁
synchronized 总是非公平的——新来的线程可能插队抢到锁,老老实实排队的线程反而更慢;而 ReentrantLock 允许构造时传 true 启用公平模式:new ReentrantLock(true)。
- 公平锁保证“先来先得”,适合对响应时间一致性要求高的场景(如金融交易指令排序)
- 但公平锁性能通常比非公平锁差,因为要维护等待队列、额外 CAS 开销,高并发下吞吐下降明显
- 默认构造(
new ReentrantLock())是非公平的,行为和synchronized接近,这也是它日常性能不输的原因之一
一个锁配多个 Condition,synchronized 只能 wait/notifyAll
synchronized 的线程协作只能靠对象的 wait()/notify()/notifyAll(),但它们共用同一组等待队列,唤醒是“全量”或“随机一个”,没法区分条件类型;ReentrantLock 可通过 lock.newCondition() 创建多个独立 Condition 实例。
立即学习“Java免费学习笔记(深入)”;
- 比如生产者-消费者模型中,可以分别定义
notFull和notEmpty两个条件,精准唤醒对应角色线程 - 避免
notifyAll()唤醒所有线程却只有 1 个能干活,其余又立刻wait()的无效调度 - 这在实现复杂状态机、多阶段任务协调时,是
synchronized根本无法替代的能力
真正容易被忽略的不是“哪个功能多”,而是「锁生命周期管理责任转移」:从 JVM 托管变成开发者自己扛。哪怕只多写两行 lock() 和 unlock(),漏掉一次,整个模块就可能静默卡死——这不是 bug,是契约。所以除非真需要中断、公平、多条件这些能力,否则优先用 synchronized 更稳妥。










