923 字
5 分钟
synchronized 与 ReentrantLock:功能、性能与使用边界

两种锁分别是什么#

synchronized 是 Java 关键字,通过 JVM Monitor 实现同步,可修饰方法和代码块,锁的获取与释放由 JVM 管理。ReentrantLock 是 JUC 提供的可重入锁,基于 AQS 实现,需要显式调用 lock() 与 unlock()。

synchronized 在字节码与 Monitor 层怎样工作?#

同步代码块通常通过 monitorenter 和 monitorexit 指令表达,编译器还会安排异常退出时的释放路径。同步方法通常通过 ACC_SYNCHRONIZED 方法标志表达,由 JVM 在调用和退出时管理同步,而不是在方法体中照搬同样的指令序列。

监视器语义可以用三个概念理解:当前持有者、重入计数、等待协调。同一线程再次进入同一个监视器可以重入,退出时逐步减少持有次数;完全释放后其他线程才有机会获取。调用 wait 会释放该监视器并进入等待,返回前重新获取;notify 只是通知等待线程,不立即交出锁。

线程竞争同一监视器
├─ 可以获取:进入临界区,或由持有者重入
└─ 不能获取:按 JVM 策略竞争或等待
临界区退出/异常退出 → 释放持有 → 其他线程再竞争

这里描述的是同步语义。HotSpot 可以使用快速路径、轻量级锁、自旋等优化,并不代表每次同步调用都创建或进入一个重量级 Monitor,也不需要第一轮背内部队列字段和对象头位数。

核心差异#

维度synchronizedReentrantLock
形态关键字类
释放方式JVM 自动释放必须手动释放
公平性非公平默认非公平,也可配置公平
扩展能力基础同步支持中断、超时获取等能力
底层机制JVM MonitorAQS

JDK 6 之后,synchronized 经历了多项锁优化;两者没有简单的“谁一定更快”。只有确实需要公平锁、可中断获取或超时等能力时,再选择 ReentrantLock。

Condition 与锁优化需要掌握什么?#

ReentrantLock 可以创建多个 Condition,分别组织不同条件的等待和通知。await/signal 等操作要持有对应锁,等待条件同样应在循环中检查;await 会释放并在返回前重新获取对应锁。

synchronized 的实现存在多种优化,常见面试会提到自旋、轻量级锁等;具体策略随 JDK 演进,不能把“无锁 → 偏向 → 轻量 → 重量”当作现代所有 JDK 必经的固定路径。第一轮重点放在锁对象、互斥、可见性和释放责任。

两个线程按相反顺序获取多把锁可能发生死锁。减少嵌套、统一锁顺序、必要时超时获取可降低风险。锁范围应覆盖不变量,但不要无必要地把慢网络调用放进长时间临界区。

常见追问#

公平锁为什么不是默认选择?#

公平锁按等待顺序获取锁;非公平锁允许新线程先 CAS 尝试抢锁,失败才排队。非公平锁减少了唤醒队首线程和上下文切换的开销,因此通常吞吐量更高。

忘记 unlock 会怎样?#

锁不会释放,其他线程会持续阻塞。ReentrantLock 应放在 try-finally 中释放:

lock.lock();
try {
// 临界区
} finally {
lock.unlock();
}

什么是可重入?#

同一线程已持有一把锁时,可以再次获取它而不发生死锁。锁内部记录持有线程和重入次数;每次获取递增、每次释放递减,计数归零后才真正释放。

synchronized 与 ReentrantLock:功能、性能与使用边界
https://blog.xqcherry.top/posts/interview/java/synchronized-vs-reentrantlock/
作者
xqcherry
发布于
2026-09-24
许可协议
CC BY-NC-SA 4.0