synchronized 的内部锁协议是 jvm 内置的协作机制,围绕 monitor、对象头 mark word 状态管理及字节码指令协同工作,通过锁状态动态升级实现高效线程安全。

Java 中 synchronized 的内部锁协议不是由开发者定义的接口或 API,而是 JVM 内置的一套协作机制,它围绕 对象监视器(Monitor) 展开,结合 对象头(Mark Word)状态管理、字节码指令控制 和 锁状态动态升级 三者协同工作。这套协议不暴露给用户代码,但行为严格、可预测,核心目标是:在保证线程安全的前提下,尽可能减少同步开销。
synchronized 锁协议的核心组成
-
Monitor 是锁的执行主体
每个 Java 对象隐式关联一个 Monitor(由 JVM 管理,底层最终依赖操作系统互斥量 Mutex)。线程要进入synchronized保护的区域,本质就是尝试获取该对象的 Monitor 所有权。Monitor 内部维护:- 一个持有者线程字段(owner)
- 一个竞争等待队列(EntryList),存放阻塞中、等待获取锁的线程
- 一个等待集合(WaitSet),存放调用
wait()后释放锁并挂起的线程
-
对象头 Mark Word 是锁的状态记录仪
HotSpot 中对象头的 Mark Word 会随锁状态动态复用存储内容:- 无锁态:存哈希码、GC 分代年龄等
- 偏向锁态:存偏向线程 ID + epoch + 是否偏向标志
- 轻量级锁态:存指向当前线程栈中 Lock Record 的指针
- 重量级锁态:存指向 Monitor 对象的指针
这种紧凑设计让锁状态变更无需额外内存分配,仅靠 CPU 原子操作(如 CAS)即可完成。
-
字节码指令是锁生命周期的控制器
deep-java-review下载Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 同步代码块 → 编译为
monitorenter/monitorexit指令monitorenter触发 Monitor 获取逻辑;monitorexit在正常出口和所有异常出口处都插入,确保锁必定释放(JVM 自动保障,无需人工干预)。 - 同步方法 → 编译时添加
ACC_SYNCHRONIZED标志
JVM 在方法调用入口自动执行 Monitor 获取,在方法返回(含异常)时自动释放。
- 同步代码块 → 编译为
锁协议的运行流程(以同步代码块为例)
- 线程 A 执行到
synchronized(obj):- 查看
obj的 Mark Word 当前状态 - 若为无锁态 → 尝试 CAS 将自身线程 ID 写入(偏向锁建立)或设置轻量级锁标记(带 Lock Record 地址)
- 若为偏向锁且属于当前线程 → 直接进入临界区(零开销)
- 若为偏向锁但被其他线程持有 → 触发偏向撤销,升级为轻量级锁
- 若竞争激烈、自旋失败 → 升级为重量级锁,线程 A 进入 EntryList 阻塞挂起
- 查看
- 线程 B 同时尝试进入同一
synchronized(obj):- 发现 Monitor 已被占用 → 根据当前锁状态决定是自旋等待(轻量级)还是直接挂起(重量级)
- 线程 A 执行完退出:
- 执行
monitorexit→ 更新 Mark Word,唤醒 EntryList 中一个线程(或通知 WaitSet) - 若锁处于重量级状态,唤醒由操作系统调度完成;轻量级/偏向锁则纯 JVM 层处理
- 执行
锁协议的关键设计原则
可重入性内建支持
Monitor 记录持有线程和重入计数(recursion count),同一线程多次进入同一锁不会死锁,每次monitorenter加一,每次monitorexit减一,归零才真正释放。-
原子性、可见性、有序性三位一体保障
- 原子性:Monitor 排他机制天然保证临界区串行执行
- 可见性:解锁前强制刷写本地内存到主内存;加锁前强制清空本地变量副本(JMM 规范要求)
- 有序性:
monitorenter/monitorexit指令天然形成内存屏障(LoadStore + StoreLoad),禁止跨边界重排序
无手动管理,不可中断,非公平
锁获取与释放完全由 JVM 控制,不支持超时等待、中断响应或公平排队策略——这是协议简化与性能权衡的结果。
不复杂但容易忽略
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










