reentrantlock与synchronized本质区别在于锁的控制粒度和生命周期管理方式不同:前者是显式对象需手动加解锁,后者是jvm隐式实现自动管理。

ReentrantLock 和 synchronized 本质区别在哪
根本不是“哪个更高级”,而是「锁的控制粒度」和「锁的生命周期管理方式」不同。synchronized 是 JVM 层硬编码的隐式协议,编译后直接转成 monitorenter/monitorexit 字节码;ReentrantLock 是 Java 类库提供的显式对象,所有行为都走普通方法调用路径。
这意味着:synchronized 的加锁/释放完全由 JVM 在进入/退出同步块时自动完成,哪怕发生异常也不会漏释放;而 ReentrantLock 必须手动 lock() 和 unlock(),漏掉 unlock() 就是死锁温床。
什么时候必须用 ReentrantLock,不能用 synchronized
三个典型场景,synchronized 确实无能为力:
- 需要尝试获取锁但不阻塞(
tryLock()),比如做非阻塞重试或快速失败逻辑 - 需要响应中断(
lockInterruptibly()),比如任务被 cancel 时及时释放资源 - 需要绑定多个
Condition实现精准唤醒(比如生产者只唤醒消费者,而不是用notifyAll()盲目通知)
注意:synchronized 永远只能对应一个隐式条件队列,唤醒靠 notify()/notifyAll(),没法区分角色。
立即学习“Java免费学习笔记(深入)”;
ReentrantLock 用错最常见的三处坑
新手写 ReentrantLock 几乎必踩:
-
unlock()没写在finally块里 —— 一旦临界区抛异常,锁就永远卡住 - 把
lock()放在try块里 —— 异常发生在加锁前,finally里调unlock()会抛IllegalMonitorStateException - 误以为
ReentrantLock是“可重入”就等于“线程安全” —— 它只保单个锁对象的重入性,不解决复合操作(如先 get 再 put)的原子性问题
正确写法骨架必须是:lock.lock(); try { ... } finally { lock.unlock(); }
性能差异没你想的那么大,但开销类型不同
JDK 6 之后 synchronized 做了大量优化(偏向锁、轻量级锁、锁膨胀),在无竞争或低竞争场景下,它比 ReentrantLock 更快,因为少了方法调用和对象创建开销;但在高竞争、需要复杂控制逻辑的场景,ReentrantLock 的可配置性(如公平锁开关、超时机制)反而让整体吞吐更可控。
公平锁(new ReentrantLock(true))会显著降低吞吐量,因为它强制排队,禁用 CPU 自旋优化 —— 除非业务真有强顺序要求,否则默认用非公平锁。
别为了“听起来更可控”就默认选 ReentrantLock;也别因“synchronized 简单”就硬套在需要中断响应或条件等待的地方。










