synchronized
是 Java 中实现线程同步的核心机制,其底层实现依赖于 JVM 的监视器(Monitor)和对象头(Object Header)结构。以下是其实现机制的详细解析:
一、核心实现原理
Monitor 监视器机制
• 每个 Java 对象关联一个 Monitor(监视器),用于控制线程对临界区的访问。
• Monitor 通过monitorenter
和monitorexit
指令实现锁的获取与释放。当线程进入同步代码块时,会尝试获取对象的 Monitor;若 Monitor 被其他线程持有,则当前线程进入阻塞状态。对象头与锁状态
• 对象头包含 Mark Word(标记字段),存储锁状态、哈希码、GC分代年龄、线程持有的锁、偏向线程 ID、偏向时间戳等信息。
• 锁状态通过 Mark Word 中的标记位动态切换,支持无锁、偏向锁、轻量级锁、重量级锁四种模式。
二、锁升级机制(JDK 1.6+)
JVM 通过锁升级优化性能,根据竞争程度动态调整锁策略:
1. 无锁(Non-locked)
• 状态标识:Mark Word 中锁标志位为 01
,偏向锁标志位为 0
。
• 适用场景:对象刚创建时,或无线程竞争的长期无锁状态。
• 实现细节:
• 线程通过 CAS 操作直接修改对象头 Mark Word 获取锁。
• 无额外同步开销,适用于单线程访问场景。
2. 偏向锁(Biased Locking)
• 状态标识:Mark Word 中锁标志位为 01
,偏向锁标志位为 1
。
• 核心机制:
• 首次加锁:线程首次进入同步块时,JVM 通过 CAS 将线程 ID 写入 Mark Word,标记为偏向锁。
• 重复访问:同一线程再次进入时,直接比对 Mark Word 中的线程 ID,无需同步操作。
• 撤销条件:当其他线程尝试竞争时,触发偏向锁撤销(需全局安全点暂停线程),升级为轻量级锁。
• 优化点:
• 减少无竞争场景的同步开销(如单线程循环内多次加锁)。
• JDK 15+ 默认禁用偏向锁(通过 -XX:-UseBiasedLocking
可手动启用)。
3. 轻量级锁(Lightweight Locking)
• 状态标识:Mark Word 中锁标志位为 00
,指向线程栈中的锁记录(Lock Record)。
• 核心机制:
• 加锁:线程在栈帧中创建 Lock Record,通过 CAS 将 Mark Word 指向该记录。
• 自旋锁(Spin Lock):
◦ 实现:CAS 失败时,线程在用户态循环尝试获取锁(默认自旋 10 次,JDK 12+ 支持自适应自旋)。
◦ 优势:避免线程阻塞和上下文切换,适用于短暂竞争场景。
◦ 限制:自旋时间过长会浪费 CPU 资源,需结合自适应策略动态调整。
• 升级条件:自旋失败次数超过阈值或竞争激烈时,升级为重量级锁。
4. 重量级锁(Heavyweight Locking)
• 状态标识:Mark Word 中锁标志位为 10
,指向 ObjectMonitor
监视器。
• 核心机制:
• 阻塞等待:线程通过操作系统互斥量(Mutex)进入阻塞状态,由 OS 调度唤醒。
• 队列管理:使用 EntryList
(阻塞队列)和 WaitSet
(等待队列)管理线程状态。
• 性能影响:上下文切换开销大,适用于高竞争或长时间持有锁的场景。
• 优化点:
• 锁消除(JIT 编译器移除无竞争锁)。
• 锁粗化(合并连续锁操作减少开销)。
三、锁的存储与同步范围
同步范围
• 实例方法:锁对象为当前实例(this
)。
• 静态方法:锁对象为类的Class
对象(如String.class
)。
• 代码块:锁对象由synchronized(obj)
显式指定。线程状态流转
• Contention List:所有请求锁的线程初始队列。
• Entry List:竞争候选线程队列(优先级较高的线程)。
• OnDeck:当前竞争锁的线程(单线程竞争)。
• Owner:持有锁的线程。
• Wait Set:通过wait()
进入等待的线程队列。
四、关键特性
可重入性(Reentrancy)
• 同一线程可多次获取同一把锁(如递归方法),避免死锁。线程阻塞与唤醒
• 通过wait()
/notify()
/notifyAll()
实现线程间协作,需配合synchronized
使用。JVM 优化措施
• 锁消除:编译器消除无竞争场景的锁。
• 锁粗化:合并连续的锁操作以减少开销。不可逆性
• 锁状态只能从无锁 → 偏向锁 → 轻量级锁 → 重量级锁单向升级,无法降级。
五、与 Lock 的对比
特性 | synchronized |
Lock 接口 |
---|---|---|
实现层级 | JVM 内置锁 | 需手动实现(如 ReentrantLock ) |
性能 | 低竞争时高效,高竞争依赖 OS | 自旋锁、公平锁等灵活策略 |
中断响应 | 不支持中断等待 | 支持 lockInterruptibly() |
条件变量 | 依赖 wait() /notify() |
通过 Condition 精细化控制 |
六、使用建议
- 优先使用轻量级锁:通过减少锁粒度、避免长时间持有锁优化性能。
- 避免嵌套锁:减少死锁风险。
- 监控锁竞争:通过 JVM 工具(如
jstack
)分析锁阻塞问题。
通过理解其底层机制和优化策略,可以更高效地设计并发程序。