synchronized 和 ReentrantLock 的区别详解
📌 synchronized 和 ReentrantLock 的区别详解
1️⃣ 问题背景
在多线程环境下,多个线程同时访问共享资源时,会产生线程安全问题。
例如:
- 多个线程同时修改库存
- 多个线程同时更新账户余额
- 多个线程同时写入缓存
- 多个线程同时生成订单号
如果不进行同步控制,就会导致数据不一致、脏数据、超卖等问题。
Java 提供了两种最常见的同步机制:
- synchronized
- ReentrantLock
面试中经常会问:
答案是:
synchronized 能解决线程安全问题,但功能相对简单;ReentrantLock 在此基础上扩展了更多高级能力。
2️⃣ 核心原理
synchronized 原理
synchronized 是 JVM 提供的关键字。
每个 Java 对象都关联一个 Monitor(监视器锁)。
线程进入 synchronized 代码块时:
↓
获得对象锁
↓
执行临界区代码
↓
Monitor Exit
↓
释放对象锁
底层依赖 JVM 的 monitorenter 和 monitorexit 指令实现。
ReentrantLock 原理
ReentrantLock 位于:
底层基于 AQS(AbstractQueuedSynchronizer)实现。
AQS 维护:
- state 状态值
- CLH 双向队列
- CAS 自旋操作
- LockSupport park/unpark
获取锁时通过 CAS 修改 state。
获取失败则进入同步队列等待。
3️⃣ 数据结构分析
synchronized
对象头(Mark Word)中保存锁信息。
├── Mark Word
├── Klass Pointer
Mark Word 会记录:
- 偏向锁信息
- 轻量级锁信息
- 重量级锁信息
- 线程ID
- 锁标志位
ReentrantLock
核心结构:
↓
Sync
↓
AQS
↓
state
↓
CLH Queue
其中:
- state = 0 表示无锁
- state > 0 表示已经被占用
- state 值表示重入次数
4️⃣ 算法分析
synchronized 算法
JDK1.6 之后进行了大量优化:
- 偏向锁
- 轻量级锁
- 自旋锁
- 重量级锁
锁会根据竞争情况自动升级。
↓
偏向锁
↓
轻量级锁
↓
重量级锁
锁只能升级,不能降级。
ReentrantLock 算法
核心流程:
↓
成功
↓
获得锁
失败:
↓
加入CLH队列
↓
阻塞等待
↓
被唤醒继续竞争
5️⃣ 执行流程
synchronized 使用方式
count++;
}
JVM 自动完成:
- 加锁
- 释放锁
- 异常处理
ReentrantLock 使用方式
public void add() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
必须手动释放锁。
否则可能发生死锁。
6️⃣ 实际案例
案例一:公平锁
true 表示公平锁。
线程按照进入队列顺序获取锁。
synchronized 无法实现公平锁。
案例二:超时等待锁
//业务逻辑
}
等待 3 秒仍未获取锁则放弃。
避免线程永久阻塞。
案例三:可中断锁
线程等待锁期间可响应 interrupt。
synchronized 无法实现。
7️⃣ 优缺点分析
| 对比项 | synchronized | ReentrantLock |
|---|---|---|
| 实现层面 | JVM | JDK(AQS) |
| 自动释放锁 | 支持 | 不支持 |
| 公平锁 | 不支持 | 支持 |
| 可中断 | 不支持 | 支持 |
| 超时获取锁 | 不支持 | 支持 |
| Condition条件队列 | 不支持 | 支持 |
| 易用性 | 高 | 一般 |
| 性能 | JDK1.6后接近 | JDK1.6后接近 |
synchronized 优点
- 语法简单
- 自动释放锁
- 不容易写出死锁代码
- JVM优化成熟
ReentrantLock 优点
- 支持公平锁
- 支持超时机制
- 支持可中断获取锁
- 支持多个 Condition
- 扩展能力强
8️⃣ 面试常见问题
1. synchronized 和 ReentrantLock 哪个性能更高?
JDK1.6 之后 synchronized 已经过大量优化,两者性能差距已经很小。
2. ReentrantLock 为什么叫可重入锁?
同一个线程已经持有锁后,可以再次获取该锁,不会发生死锁。
3. ReentrantLock 底层是什么实现的?
AQS + CAS + CLH 双向队列。
4. synchronized 为什么也是可重入锁?
Monitor 中记录持有锁线程和重入次数,同一个线程再次进入同步代码块时只增加计数即可。
5. 为什么 ReentrantLock 必须放 finally?
防止业务异常导致 unlock 未执行,从而产生死锁。
如果只需要简单同步,优先使用 synchronized;如果需要公平锁、超时锁、可中断锁、多个条件队列等高级功能,则使用 ReentrantLock。
9️⃣ 总结
本质区别:
① synchronized 是 JVM 内置锁,底层基于 Monitor。
② ReentrantLock 是 JDK 实现锁,底层基于 AQS。
③ synchronized 自动加锁、自动释放锁。
④ ReentrantLock 需要手动 lock/unlock。
⑤ ReentrantLock 提供公平锁、超时锁、可中断锁等高级能力。
⑥ JDK1.6 后 synchronized 经过偏向锁、轻量级锁、自旋锁等优化,性能已经非常接近 ReentrantLock。
实际开发经验:
业务开发优先 synchronized;框架、中间件、高并发组件开发优先 ReentrantLock。
相关文章
-
秒杀项目常见面试题
希望将过去所学的一些知识进行系统化的深入理解。秒杀项目应用场景丰富,涉及高并发、缓存、消息队列、分布式锁等多个中间件,更有利于对 Web 服务架构的深入学习。
NEW个对象 2026-06-12
-
降级条件设置代码如何实现?从手写熔断器到 Sentinel 实战详解
降级条件设置代码如何实现?从手写熔断器到 Sentinel 实战详解
NEW个对象 2026-06-12
-
Sentinel支持哪些降级规则?原理、源码思想与生产实践详解
Sentinel支持哪些降级规则?原理、源码思想与生产实践详解
NEW个对象 2026-06-12