目录
一、先明确核心前提:synchronized 的 “锁” 到底是什么?
二、synchronized 的底层基石:对象头与 Monitor
前言
在 Java 并发编程中,synchronized是最基础也最常用的同步手段,但它的底层原理(尤其是锁升级、轻量级锁的 CAS 竞争逻辑)往往让初学者困惑。本文结合我在学习过程中与AI的对话,完整梳理synchronized的底层逻辑,重点突破轻量级锁的核心难点,希望能够帮助到大家。
一、先明确核心前提:synchronized 的 “锁” 到底是什么?
synchronized的本质是 “让多线程排队访问共享资源”,而实现这一逻辑的核心载体是 “锁对象”—— 任何 Java 对象(如new Object()、类对象、实例对象)都自带一把 “锁”,synchronized正是通过 “争抢这把锁” 控制线程同步。
关键结论:
- 当你写
synchronized(lockObj) { ... }时,争抢的是lockObj这把 “锁”; - 当你用
synchronized修饰实例方法时,争抢的是this(当前实例)这把 “锁”; - 当你用
synchronized修饰静态方法时,争抢的是 “类对象(如 Xxx.class)” 这把 “锁”。
二、synchronized 的底层基石:对象头与 Monitor
要理解锁的实现,必须先搞懂两个核心结构:对象头和Monitor(管程)—— 它们是synchronized实现同步的 “硬件基础”。
1. 对象头:锁状态的 “存储区”
每个 Java 对象在内存中都包含三部分:对象头 + 实例数据(成员变量等) + 对齐填充。其中,对象头是锁状态的 “记录本”,而synchronized锁定的 “锁对象” 的对象头,正是存储锁信息的关键。
对象头中有一个核心区域叫Mark Word(标记字段),它的结构会随 “锁状态” 动态变化(以 32 位 JVM 为例):
| 锁状态 | Mark Word 存储内容 | 核心作用 |
|---|---|---|
| 无锁 | 对象哈希码 + 分代年龄 | 记录对象基本信息 |
| 偏向锁 | 偏向的线程 ID + 分代年龄 | 标记 “锁偏爱哪个线程” |
| 轻量级锁 | 指向线程栈中 “锁记录(Lock Record)” 的指针 | 关联持有锁的线程 |
| 重量级锁 | 指向 “Monitor(管程)” 的指针 | 交给管理员管理线程竞争 |
关键疑问解答:对象头指的是 “锁对象的对象头吗?”是的!只有被synchronized锁定的 “锁对象”,其对象头的 Mark Word 才会记录锁状态 —— 离开锁对象的对象头,锁的状态管理、线程竞争都无从谈起。
2. Monitor:锁的 “管理员”
每个 Java 对象都隐式关联一个Monitor(可理解为 “锁的管理员”,由 C++ 实现,如 HotSpot 中的ObjectMonitor),它负责管理线程的 “抢锁” 和 “排队”,内部包含三个核心结构:
- Owner:记录当前持有锁的线程(初始为 null);
- EntryList:未抢到锁的线程排队的队列(线程阻塞状态);
- WaitSet:调用
wait()后暂时 “离场” 的线程队列(需notify()唤醒后重新排队); - Recursion Count:锁的重入计数(同一线程多次抢锁时累加,减到 0 才释放)。
Monitor 的核心逻辑:当线程尝试抢锁时,会向 Monitor “申请权限”:
- 若 Owner 为 null,线程直接成为 Owner(Recursion Count=1);
- 若 Owner 是当前线程,Recursion Count+1(可重入);
- 若 Owner 是其他线程,当前线程进入 EntryList 阻塞,等待 Owner 释放锁。
三、synchronized 的性能优化:锁升级机制(核心重点)
JDK 1.6 之前,synchronized直接使用 “重量级锁”(依赖操作系统互斥量,线程阻塞 / 唤醒开销大),性能较差。JDK 1.6 后引入 “锁升级机制”—— 根据 “竞争激烈程度” 动态切换锁状态,平衡 “安全性” 和 “性能”。
锁升级的完整路径:无锁 → 偏向锁 → 轻量级锁 → 重量级锁(不可逆,只能升级不能降级)。
1. 偏向锁:无竞争时的 “专属锁”
核心场景
只有一个线程反复获取锁(无其他线程竞争),比如单线程执行同步代码。
实现逻辑
- 线程 A 第一次抢锁时,JVM 通过 CAS 操作,将 “线程 A 的 ID” 写入锁对象的 Mark Word(Mark Word 进入 “偏向锁状态”
- 线程 A 之后再抢锁,只需检查 Mark Word 中的线程 ID 是否为自己:若是,直接进入代码(无需任何 CAS 操作,几乎零开销);
- 若有其他线程(如 B)尝试抢锁,偏向锁会被 “撤销”,升级为轻量级锁。
关键细节:偏向锁有锁记录吗?
没有!锁记录(Lock Record)是轻量级锁的结构,而偏向锁的设计目标是 “无竞争场景下的极致简化”—— 直接通过 Mark Word 记录线程 ID,无需在线程栈中创建额外结构。
2. 轻量级锁:低竞争时的 “自旋抢锁”
核心场景
少量线程竞争锁,且每个线程持有锁的时间短(比如线程执行同步代码仅需几微秒)。
核心逻辑
当偏向锁被撤销(有线程 B 参与竞争),锁升级为轻量级锁,此时锁对象的 Mark Word 存储 “指向持有锁线程栈中锁记录的指针”(比如指向线程 A 的锁记录)。
下面结合 “线程 A 持有锁,线程 B 抢锁” 的完整流程,拆解轻量级锁的核心难点:
步骤 1:线程 A 如何持有轻量级锁?
- 线程 A 执行同步代码前,在自己的 “线程栈”中创建一块空间 ——锁记录(Lock Record);
- 线程 A 将锁对象当前的 Mark Word(此时可能是偏向锁状态或无锁状态)拷贝到自己的锁记录中;
- 线程 A 通过 CAS 操作,将锁对象的 Mark Word 改为 “指向自己锁记录的指针”——CAS 成功,线程 A 持有轻量级锁,进入代码执行。
步骤 2:线程 B 如何 “自旋 + CAS” 抢锁?
当线程 B 执行到同步代码时,发现锁被 A 持有(Mark Word 指向 A 的锁记录),会启动 “自旋抢锁” 流程:
- 创建锁记录:线程 B 在自己的栈中创建锁记录,拷贝当前 Mark Word(即 “指向 A 锁记录的指针”),并记录锁对象地址;
- CAS 尝试抢锁:线程 B 通过 CAS,尝试将锁对象的 Mark Word 改为 “指向自己锁记录的指针”—— 此时分两种情况:
情况 1:A 仍在执行代码(未释放锁),B 的 CAS “表面成功但实际无效”
- B 的 CAS 看似成功(Mark Word 改成了指向 B 的锁记录),但 JVM 会强制 B 做 “归属权检查”;
- B 通过自己锁记录中 “拷贝的 Mark Word”(指向 A 的锁记录的指针),找到 A 的锁记录,发现 A 的锁记录中 “持有线程” 仍为 A(A 还在执行代码);
- JVM 判定 B 的 CAS 无效,将 Mark Word 回滚为 “指向 A 的锁记录”,B 继续自旋重试(默认自旋 10 次,避免立即阻塞)。
情况 2:A 准备释放锁,B 的 CAS “真正成功”
- A 执行完代码后,先 “放弃归属权”:将自己锁记录中的 “持有线程” 改为 null
- A 开始执行 “释放 CAS”(试图将 Mark Word 改回原始状态),但 B 的 CAS 比 A 快一步,先将 Mark Word 改为 “指向自己的锁记录”;
- JVM 强制 B 做归属权检查:B 通过指针找到 A 的锁记录,发现 “持有线程” 为 null(A 已释放);
- JVM 判定 B 的 CAS 有效,B 在自己的锁记录中标记 “持有线程为 B”,成功获锁并执行代码。
步骤 3:轻量级锁何时升级为重量级锁?
若线程 B 自旋到阈值(如 10 次)仍未抢到锁,或有更多线程(如 C、D)参与竞争,JVM 会判定 “竞争超出轻量级锁的承载能力”,触发锁膨胀:
- 将锁对象的 Mark Word 改为 “指向 Monitor 的指针”(轻量级锁→重量级锁);
- 线程 B 停止自旋,进入 Monitor 的 EntryList 阻塞,等待管理员叫号(A 释放锁后,Monitor 唤醒队列中的线程)。
轻量级锁的关键特性
- 可重入性:线程持有锁后,进入嵌套同步代码时,只需将锁记录的 “重入计数”+1,无需再次 CAS;
- 自旋阈值:默认由 JVM 控制(如 10 次或 CPU 核心数的一半),避免无意义的 CPU 空转;
- 安全保障:通过 “归属权检查” 确保同一时间只有一个线程持有锁,不会出现 “两个线程同时执行同步代码” 的问题。
3. 重量级锁:高竞争时的 “阻塞排队”
核心场景
多线程激烈竞争锁,或线程持有锁的时间长(比如同步代码中包含 IO 操作)。
实现逻辑
- 锁对象的 Mark Word 指向 Monitor(管理员);
- 线程抢锁时,若 Owner 已被占用,直接进入 EntryList 阻塞(放弃 CPU,不再自旋);
- 持有锁的线程释放锁后,Monitor 从 EntryList 中唤醒一个线程,将 Owner 交给它(非公平,新线程可能插队)。
特点
- 优点:避免自旋浪费 CPU,适合长时间持有锁或高竞争场景;
- 缺点:线程阻塞 / 唤醒需要切换内核态,开销比轻量级锁大。
四、synchronized 的核心特性总结
- 可重入性:同一线程可多次获取同一把锁(通过 Monitor 的 Recursion Count 实现),避免自己阻塞自己;
- 非公平性:锁释放后,等待队列中的线程不保证按顺序获锁(新线程可能直接抢占,提升吞吐量);
- 渐进式优化:通过 “偏向锁→轻量级锁→重量级锁” 的升级,动态适配竞争强度,平衡安全与性能。
五、面试小贴士:如何清晰回答 synchronized 底层原理?
建议按 “载体→机制→优化” 的逻辑回答,结合通俗比喻:
- 先讲核心载体:
synchronized基于 “锁对象” 实现,锁的状态存在 “锁对象的对象头(Mark Word)” 中,由 “Monitor” 管理线程竞争; - 再讲锁升级机制:从偏向锁(无竞争,记线程 ID)到轻量级锁(低竞争,自旋 + CAS),再到重量级锁(高竞争,阻塞排队);
- 重点拆解轻量级锁:讲清 “锁记录的创建→CAS 抢锁→归属权检查→回滚 / 成功” 的流程,体现对细节的理解。
synchronized锁升级全解析
1209

被折叠的 条评论
为什么被折叠?



