将用一个"魔法城堡"的故事,带你深入理解 synchronized 的底层实现原理。这座城堡有一道神奇结界,保护着珍贵的宝物(共享资源),而这道结界的运作机制正是 synchronized 的完美隐喻!
🏰 故事背景:魔法城堡的结界系统
想象一座神奇的魔法城堡:
- 宝物房间 = 共享资源
- 魔法师 = 线程(Thread)
- 结界 = synchronized 关键字
- 结界之书 = 对象头(Object Header)
- 守护精灵 = 监视器(Monitor)
每个宝物房间(对象)都有一本结界之书(对象头),记录着结界的当前状态。魔法师(线程)想进入房间时,必须通过结界系统的验证!
📜 第一章:结界之书(对象头结构)
每个Java对象都有隐藏的结界之书(对象头),包含关键信息:
java
// 64位JVM对象头结构 (简化表示)
|-------------------------------------------------------|
| Mark Word (64 bits) |
|-------------------------------------------------------|
| unused:25 | identity_hashcode:31 | unused:1 | age:4 | biased_lock:1 | lock:2
|-------------------------------------------------------|
| ptr_to_monitor:62 | lock:2
|-------------------------------------------------------|
| ptr_to_heavyweight_monitor:62 | lock:2
|-------------------------------------------------------|
🧙 结界状态解读:
-
无锁状态(001):
- 存储对象的hashCode和分代年龄
- 任何魔法师都可尝试进入
-
偏向锁(101):
- 存储持有线程ID和epoch
- 城堡记住常客,快速通行
-
轻量级锁(00):
- 存储指向栈中锁记录的指针
- 魔法师们友好竞争
-
重量级锁(10):
- 存储指向监视器(Monitor)的指针
- 需要守护精灵管理队列
🔄 第二章:结界升级之路(锁膨胀过程)
场景1:无竞争 → 偏向锁(Biased Locking)
java
public class TreasureRoom {
private int gold = 1000;
public synchronized void addGold(int amount) {
gold += amount; // 只有一个魔法师常来
}
}
结界运作:
- 第一个魔法师A进入房间
- 结界记录魔法师ID到结界之书(偏向锁)
- 魔法师A后续进入只需验证ID,直接通行(无CAS)
⏳ 偏向锁撤销:
当魔法师B尝试进入:
- 检查结界之书,发现已被A占据
- 暂停魔法世界(STW安全点)
- 若A仍在房间 → 升级为轻量级锁
- 若A已离开 → 重置为无锁状态
场景2:轻度竞争 → 轻量级锁(Lightweight Locking)
java
public void transfer(TreasureRoom from, TreasureRoom to, int amount) {
synchronized(from) {
synchronized(to) { // 两个魔法师交替访问
from.gold -= amount;
to.gold += amount;
}
}
}
结界运作:
-
魔法师A进入前,在栈上创建锁记录(Lock Record)
-
复制结界之书内容到锁记录(Displaced Mark Word)
-
用CAS尝试将对象头指向锁记录:
java
if (CAS(objectMarkWord, expected: unbiasedPattern, newValue: pointerToLockRecord)) { // 获取轻量级锁成功 } else { // 升级为重量级锁 } -
成功:进入房间(锁标志位00)
-
失败(竞争):膨胀为重量级锁
场景3:激烈竞争 → 重量级锁(Heavyweight Locking)
java
public class Castle {
public static void main(String[] args) {
TreasureRoom room = new TreasureRoom();
// 100个魔法师争夺宝物
for (int i = 0; i < 100; i++) {
new Thread(() -> {
synchronized(room) {
room.addGold(10);
}
}).start();
}
}
}
结界运作:
- 轻量级锁竞争失败
- 召唤守护精灵(ObjectMonitor)
- 结界之书记录监视器地址(锁标志位10)
- 未获锁的魔法师进入等待队列
👁️ 第三章:守护精灵的真身(ObjectMonitor)
每个对象在锁膨胀时,都会关联一个ObjectMonitor(C++实现):
cpp
// hotspot/src/share/vm/runtime/objectMonitor.hpp
class ObjectMonitor {
volatile markOop _header; // 原始对象头备份
void* volatile _object; // 关联的对象
volatile intptr_t _count; // 重入次数
volatile intptr_t _waiters; // 等待线程数
volatile intptr_t _recursions; // 重入计数
Thread* volatile _owner; // 当前持有线程
ObjectWaiter* volatile _EntryList; // 竞争队列
ObjectWaiter* volatile _WaitSet; // 等待队列(调用wait的线程)
// 关键方法
void enter(TRAPS); // 获取锁
void exit(bool not_suspended, TRAPS); // 释放锁
};
🧚 守护精灵的工作流程:
🔐 第四章:进入结界的魔法(monitorenter字节码)
当魔法师尝试进入同步块时:
java
public void enterRoom() {
synchronized(this) { // ← 触发monitorenter
// 宝物操作
} // ← 触发monitorexit
}
编译后的字节码:
java
aload_0 // 加载this引用
dup // 复制引用
astore_1 // 存储到局部变量1
monitorenter // 进入监视器
try {
// 同步块代码...
} finally {
aload_1
monitorexit // 退出监视器
}
⚙️ monitorenter 的魔法逻辑:
-
检查结界类型:
- 偏向锁:尝试重偏向或撤销
- 轻量级锁:尝试栈锁CAS
- 重量级锁:调用ObjectMonitor::enter
-
重量级锁获取流程:
cpp
// objectMonitor.cpp void ObjectMonitor::enter(TRAPS) { if (TryLock(Self) > 0) return; // 快速获取 if (TrySpin(Self) > 0) return; // 自旋尝试 // 添加到竞争队列 EnterI(THREAD); } void ObjectMonitor::EnterI(TRAPS) { // 创建等待节点 ObjectWaiter node(Self); Self->_ParkEvent->reset(); node._prev = (ObjectWaiter*)&_EntryList; // 插入队列尾部 ObjectWaiter* tail = _EntryList; if (tail == NULL) { _EntryList = &node; } else { while (tail->_next != NULL) tail = tail->_next; tail->_next = &node; } // 阻塞当前线程 Self->_ParkEvent->park(); }
🚪 第五章:离开结界的仪式(monitorexit)
当魔法师完成宝物操作:
java
void ObjectMonitor::exit(bool not_suspended, TRAPS) {
if (_recursions != 0) { // 重入计数减1
_recursions--;
return;
}
// 唤醒等待队列
if (_EntryList != NULL) {
ObjectWaiter* w = _EntryList;
_EntryList = w->_next;
w->_thread->_ParkEvent->unpark(); // 唤醒线程
} else if (_cxq != NULL) {
// 处理cxq队列...
}
// 重置所有者
_owner = NULL;
}
🔄 重入锁的实现:
java
public class RecursiveMagic {
public synchronized void outer() {
inner(); // 重入内部同步方法
}
public synchronized void inner() {
// 宝物操作
}
}
- 每次进入:
_recursions++ - 每次退出:
_recursions-- - 仅当
_recursions==0时完全释放
⚡ 第六章:现代结界的优化(JVM黑科技)
1. 锁消除(Lock Elision)
java
public String concat(String s1, String s2) {
StringBuffer sb = new StringBuffer();
sb.append(s1); // 同步方法,但JVM检测到线程安全
sb.append(s2);
return sb.toString();
}
JVM检测到sb不会逃逸出方法,自动移除同步!
2. 锁粗化(Lock Coarsening)
java
public void addItems(List<Item> items) {
for (Item item : items) {
synchronized(this) { // 连续多次加锁解锁
inventory.add(item);
}
}
}
JVM优化为:
java
synchronized(this) {
for (Item item : items) {
inventory.add(item);
}
}
3. 自适应自旋(Adaptive Spinning)
java
// objectMonitor.cpp
int ObjectMonitor::TrySpin(Thread * Self) {
int ctr = _SpinDuration; // 动态调整的自旋次数
while (--ctr >= 0) {
if (TryLock(Self) > 0) return 1;
SpinPause(); // CPU暂停指令(避免忙等消耗)
}
return 0;
}
JVM根据历史成功率动态调整自旋次数
⚔️ 第七章:synchronized vs ReentrantLock
| 特性 | synchronized | ReentrantLock |
|---|---|---|
| 实现级别 | JVM原生(字节码指令) | JDK代码(基于AQS) |
| 锁获取 | 自动获取和释放 | 手动lock()/unlock() |
| 灵活性 | 基本功能 | 高级功能(公平锁、条件等) |
| 性能 | Java 6后大幅优化 | 高竞争下表现更好 |
| 锁升级 | 支持(无锁→偏向→轻量→重量) | 始终是重量级锁 |
| 死锁检测 | 不支持 | 提供tryLock()死锁检测 |
| 可中断 | 等待中不可中断 | lockInterruptibly()支持中断 |
| 绑定 | 与对象/方法绑定 | 独立锁对象 |
| 代码简洁性 | 简洁(语法糖) | 需try-finally保证释放 |
🧪 第八章:从源码看结界魔法
1. 对象头解析工具(JOL)
java
// 添加依赖:org.openjdk.jol:jol-core
public class HeaderInspection {
public static void main(String[] args) {
Object obj = new Object();
System.out.println(ClassLayout.parseInstance(obj).toPrintable());
synchronized(obj) {
System.out.println(ClassLayout.parseInstance(obj).toPrintable());
}
}
}
2. 输出示例(64位系统):
text
// 无锁状态
OFFSET SIZE TYPE DESCRIPTION
0 4 (object header) 01 00 00 00
4 4 (object header) 00 00 00 00
8 4 (object header) e5 01 00 f8
// 重量级锁状态
OFFSET SIZE TYPE DESCRIPTION
0 4 (object header) 5a 3b 03 03
4 4 (object header) 00 70 00 00
3. 监视器查看工具(jstack)
bash
$ jstack <pid>
"main" #1 prio=5 os_prio=0 tid=0x00007f... nid=0x7e9c
java.lang.Thread.State: BLOCKED (on object monitor)
at TreasureRoom.addGold(TreasureRoom.java:10)
- waiting to lock <0x000000076ab62d98> (a TreasureRoom)
💎 总结:synchronized 设计精髓
-
渐进式锁升级:
- 偏向锁 → 减少无竞争开销
- 轻量级锁 → 减少轻度竞争开销
- 重量级锁 → 保证高竞争稳定性
-
对象头集成:
- Mark Word 存储锁状态
- 无额外内存开销(对比ReentrantLock)
-
JVM深度优化:
- 锁消除/粗化
- 自适应自旋
- 逃逸分析支持
-
硬件级协同:
- CAS指令实现轻量级锁
- 内存屏障保证可见性
- pause指令优化自旋
🌟 魔法启示:
synchronized = 对象头锁状态 + CAS轻量竞争 + Monitor队列管理
如同魔法城堡的智能结界系统,根据访客情况自动调整防护策略,既高效又安全地守护着共享资源!
通过这个故事,你已经深入理解了 synchronized 从字节码到JVM再到操作系统的完整实现路径。下次使用 synchronized 时,想象那座魔法城堡的结界,感受Java并发设计的精妙之处吧! 🏰✨