适用读者:背过"偏向锁→轻量级锁→重量级锁"但说不清细节的人。
前置:S05-B01。
实测环境:OpenJDK 21.0.12.1(2026-11-05)。
覆盖旧文:A-1120(371) A-938(75) A-1606(89) A-2637(114) + 1607(157) 939(76) 1028(105) 940(73) 930(81) = 1,141 点。
覆盖扩展:E-S05-004(监视器与 Mark Word)、E-S05-005(偏向锁已废弃)。
上一篇:S05-B02 · 线程状态机与创建方式
下一篇:S05-B04 · ThreadLocal。
1. 一句话结论
synchronized 的实现是"对象头里的 Mark Word + 监视器(monitor)"。所谓"锁升级",是 JVM 为了省下系统调用开销而做的三级优化——它优化的是"竞争程度",不是"锁的功能"。
2. 它锁的是什么(E-S05-004)
| 修饰位置 | 锁的对象 |
|---|---|
| 实例方法 | 当前实例(this) |
| 静态方法 | Class 对象 |
| 同步代码块 | 括号里的对象 |
关键认知:锁信息在对象头(Mark Word)里,不在方法上。所以"两个线程锁不同的对象"互不阻塞。
对象头 Mark Word(64 位 JVM):
hashCode | 分代年龄 | 锁标志位 | 偏向线程 ID ...
↑ 锁状态就编码在这几个 bit 里
3. 三级"升级"
| 阶段 | 触发条件 | 代价 | 说明 |
|---|---|---|---|
| 偏向锁 | 只有一个线程反复进入 | 最低(CAS 一次记 ID) | JDK 15 起默认禁用、后续版本移除(E-S05-005) |
| 轻量级锁 | 有第二个线程竞争(短暂) | 中(CAS 自旋) | 自旋失败→膨胀 |
| 重量级锁 | 自旋失败/竞争激烈 | 高(内核互斥量,线程阻塞) | 涉及用户态/内核态切换 |
实测佐证(B01):8 线程 × 20 万次 synchronized 递增 101 ms,比无锁版本(121 ms)还快——因为低竞争下走的是轻量级路径,并没有真的进内核。
"升级不可逆":一旦膨胀为重量级,不会退回轻量级(这是常见面试点,也是"为什么不要滥用 sync"的原因之一)。
4. 与 ReentrantLock 的对比
| 维度 | synchronized |
ReentrantLock |
|---|---|---|
| 释放 | 自动(异常也释放) | 必须 finally 手动释放 |
| 可中断 | ❌ | ✅ lockInterruptibly |
| 超时 | ❌ | ✅ tryLock(timeout) |
| 公平性 | 非公平 | 可选公平 |
| 条件变量 | wait/notify(单条件) |
多 Condition |
| 实现 | JVM 层(monitor) | Java 层(AQS,J01) |
判据:需要超时/可中断/多条件 → ReentrantLock;否则用 synchronized(更简单、不会漏释放)。
5. 死锁:四个必要条件与本系列的看法
| 条件 | 破坏方式 |
|---|---|
| 互斥 | 尽量无锁(CAS/不可变) |
| 占有并等待 | 一次性申请全部资源 |
| 不可剥夺 | 支持超时(tryLock) |
| 循环等待 | 统一加锁顺序 |
排查手段:jstack <pid> 会直接输出 Found one Java-level deadlock(A02 详讲)。
注意区分:线程死锁(本系列)与数据库死锁(S11,A-2381/A-849 已移交)。
6. 五个误区
| 误区 | 真相 |
|---|---|
| "锁加在方法上就锁住了方法" | 锁的是实例/Class 对象 |
| "锁升级会降级" | 膨胀后不退回 |
| "偏向锁一直有效" | JDK 15+ 默认禁用、后续移除 |
"synchronized 一定比 ReentrantLock 慢" |
低竞争下 sync 更快(JVM 优化) |
"wait 与 sleep 一样" |
wait 释放锁,sleep 不释放 |
7. 自检
synchronized修饰实例方法/静态方法/代码块,各锁什么?- 三级升级的触发条件分别是什么?
- 为什么说"低竞争下
synchronized不慢"(用实测说明)? ReentrantLock相比synchronized多出哪三个能力?- 死锁的四个必要条件,哪个最容易在代码里被破坏?
8. 下一篇
S05-B04 · ThreadLocal:结构、场景与泄漏
锁讲完,下一篇讲"另一条路":不共享就不会有竞争——以及 ThreadLocal 为什么会导致内存泄漏。
本篇属于系列《S05 · Java 并发与 JVM》。旧文来源:A-1120/938/1606/2637/1607/939/1028/940/930;扩展点 E-S05-004/005。计时数据来自 OpenJDK 21 实测。