Java内置锁:synchronized
synchronized
在 Java 中,synchronized 是实现线程同步的核心机制,其底层原理涉及 对象头结构、Monitor 锁模型 和 锁升级优化。
一、synchronized 的作用
- 原子性:确保同一时刻只有一个线程执行同步代码。
- 可见性:线程解锁前,变量修改会强制刷入主内存;加锁时,从主内存重新加载变量。
- 有序性:通过锁限制指令重排序(遵守 as-if-serial 和 happens-before 规则)。
二、底层实现:对象头与 Monitor
- 对象头结构
每个 Java 对象在堆中存储时,对象头包含 Mark Word(存储锁状态)和 类型指针。
锁状态通过 biased_lock 和 lock 标志位标识。
2. Monitor 机制
每个对象关联一个 Monitor(由 C++ 实现的 ObjectMonitor 结构)。
Monitor 关键字段:
_owner:持有锁的线程。_EntryList:竞争锁时阻塞的线程队列。_WaitSet:调用 wait() 后进入等待状态的线程队列。
加锁过程:
线程尝试通过 CAS 操作将 Mark Word 替换为指向 Monitor 的指针。成功则获得锁;失败则进入 _EntryList 阻塞等待。
三、锁升级优化(JDK 6+)
为提高性能,JVM 会根据竞争情况动态升级锁状态:
- 偏向锁(Biased Lock)
适用场景:没有实际竞争,仅单个线程反复获取锁。实现:在 Mark Word 中记录线程 ID,后续同一线程无需 CAS 操作。
示例:
Object lock = new Object();
synchronized (lock) {
// 首次进入:升级为偏向锁
// 同步代码
}
- 轻量级锁(Lightweight Lock)
适用场景:锁竞争轻微,多线程交替执行。实现:通过 自旋(CAS) 尝试获取锁,避免线程阻塞。
synchronized (lock) {
// 存在竞争但短暂:升级为轻量级锁
for (int i = 0; i < 100; i++)
{
// 低竞争循环操作
}
}
- 重量级锁(Heavyweight Lock)
适用场景:高并发竞争,自旋消耗 CPU 资源。
实现:锁升级为重量级锁,未获取锁的线程进入阻塞状态(依赖操作系统互斥量)。
synchronized (lock)
{
// 高竞争场景:升级为重量级锁
// 高并发操作(如数据库连接池竞争)
}
四、性能优化机制
自适应自旋
JVM 根据历史自旋成功率动态调整自旋次数。
锁消除(Lock Elimination)
编译器通过逃逸分析,去除不可能存在竞争的锁(如局部变量同步)。
public void method()
{
Object localObj = new Object();
synchronized (localObj)
{
// 锁被消除
// 局部对象无并发访问
}
}
锁粗化(Lock Coarsening)
将相邻的同步块合并,减少锁的获取/释放次数。
synchronized (lock)
{
// 操作1
}
synchronized (lock)
{
// 合并为一个同步块
// 操作2
}
五、与 ReentrantLock 对比
特性 | synchronized | ReentrantLock |
实现方式 | JVM 内置实现 | 基于 AQS 的 API 实现 |
锁获取方式 | 自动加锁/解锁 | 需手动 lock() 和 unlock() |
灵活性 | 有限(非公平锁为主) | 支持公平锁与非公平锁 |
条件变量 | 通过 wait()/notify() 实现 | 通过 Condition 实现多个条件 |
锁升级优化 | 支持(偏向锁→轻量级→重量级) | 无锁升级,始终基于 AQS 竞争 |
六、总结
- 底层核心:通过对象头标记锁状态,依赖 Monitor 控制线程竞争。
- 锁升级:根据竞争激烈程度动态优化(偏向锁→轻量级→重量级)。
- 优化手段:自旋、锁消除、锁粗化降低同步开销。
- 适用场景:轻量级同步推荐 synchronized,复杂需求(如超时、公平性)使用 ReentrantLock。