---
title: synchronized 实现与三特性
type: concept
domain: java-concurrency
tags: [synchronized, monitor, mark-word, lock-escalation, biased-lock, lightweight-lock, heavyweight-lock]
status: learning
created: 2026-09-26
last_reviewed: 2026-09-26
method: feynman
related_questions:
  - "synchronized是怎么实现的？"
  - "synchronized是如何保证原子性、可见性、有序性的？"
related_knowledge:
  - ./concurrency-foundations.md
  - ./jmm-volatile-cas.md
  - ./aqs-mechanism.md
anki_cards: 6
interview_rounds:
  - "2026-09-26-round-1-Q6"
  - "2026-09-26-round-1-Q8"
  - "2026-09-29-round-1-Q4"
  - "2026-09-29-round-1-Q5"
---

# synchronized 实现与三特性

> 对象头 + Mark Word + 锁升级四条路径 + 原子性/可见性/有序性三大特性的底层机制。

## TL;DR（30 秒扫完）

- **对象头** = Mark Word（8 字节，存哈希码/GC 年龄/锁标志位/锁指针）+ Klass Pointer（4-8 字节）
- **锁升级四条路径**：无锁 → 偏向锁 → 轻量级锁 → 重量级锁（只有升级没有降级）
- **JDK 15 默认关闭偏向锁，JDK 18 彻底移除**（偏向锁撤销代价高、需 STW）
- **三大特性底层**：原子性=Monitor owner 互斥；可见性=解锁刷主存+加锁 reload；有序性=JIT 禁止指令重排
- **synchronized 是非公平锁**（JVM 内部不保证先到先得）

## 关键结论

- **结论 A**：synchronized 靠对象头 Mark Word 记录锁状态，锁升级从无锁→偏向锁→轻量级锁→重量级锁，只有升级没有降级
- **结论 B**：三大特性各自独立底层——原子性靠 Monitor owner 互斥，可见性靠解锁刷主存+加锁 reload，有序性靠 JIT 禁止指令重排
- **结论 C**：synchronized 是**非公平锁**，JVM 内部不保证先到先得，需要公平性用 `ReentrantLock(true)`
- **结论 D**：JDK 15 后偏向锁被默认关闭，JDK 18 彻底移除——偏向锁撤销代价高、需 STW，现代 GC 下不划算

## 完整讲解（费曼四步）

### STEP 1 · 概念

synchronized 是 Java 内置的同步关键字，同时保证原子性、可见性、有序性。它的底层是对象头（Mark Word）记录锁状态 + 锁升级机制 + Monitor（管程）。

### STEP 2 · 大白话

> **类比**：synchronized 就像一间只能进一个人的密室（Monitor）。门口有个门禁（CAS）——先尝试进，进不去就在门口排队（CLH 队列）。一旦进去，门就从里面锁上（owner 指针指向你），其他人只能等。
>
> 锁升级就像"逐步加严管理"：刚开始只有一个常客（偏向锁，零开销）→ 来了第二个客人（轻量级锁，CAS 自旋）→ 来了很多客人排队（重量级锁，OS 阻塞）。

### STEP 3 · 底层

#### 对象头结构（HotSpot 64 位）

| 部分 | 大小 | 内容 |
|------|------|------|
| **Mark Word** | 8 字节 | 哈希码、GC 分代年龄、**锁标志位（最后 2 位）**、锁指针/偏向线程 ID |
| **Klass Pointer** | 4-8 字节 | 指向方法区的类元数据 |
| 数组 length | 4-8 字节 | 仅数组对象有 |

**Mark Word 锁标志位**（最后 2 位）决定当前锁状态：
- `01` = 无锁
- `00` + 锁标志位 `01` = 偏向锁
- `00` = 轻量级锁
- `10` = 重量级锁
- `11` = GC 标记（标记后回到无锁）

#### 锁升级四条路径

| 阶段 | 触发条件 | 机制 | 性能 |
|------|---------|------|------|
| **无锁** | 初始状态 | — | — |
| **偏向锁** | 只有一个线程访问 | Mark Word 记录线程 ID，同线程访问直接放行，不加锁不 CAS | 最轻量（零开销） |
| **轻量级锁** | 第二个线程到达 | 撤销偏向锁，CAS 自旋尝试获取，不进入 OS 阻塞 | 中等 |
| **重量级锁** | CAS 自旋失败达阈值（默认 10 次） | 膨胀为重量级锁，线程挂起到 Monitor entryList，OS 调度 | 最重 |

**关键规则**：
- 只有升级没有降级——JVM 保证锁状态单调递增
- 偏向锁适合"一次获取、多次访问"的低竞争场景
- 轻量级锁适合"锁持有时间极短"的中竞争场景
- 重量级锁是最终兜底

#### JDK 演进

| 版本 | 变化 |
|------|------|
| JDK 6 | 引入锁升级优化（偏向锁 + 轻量级锁） |
| JDK 15 (JEP 374) | 偏向锁默认关闭（`-XX:-UseBiasedLocking`） |
| JDK 18 | 偏向锁彻底移除 |

**废弃原因**：偏向锁撤销代价高（需 STW），现代 GC（ZGC/Shenandoah）让偏向锁优化不划算；STW 时代结束偏向锁失去主要场景。

#### 三大特性的底层机制

| 特性 | 底层机制 | 关键动作 |
|------|---------|---------|
| **原子性** | Monitor 互斥 | 每个对象隐含 Monitor，含 owner 指针；CAS 尝试设置 owner；重量级锁下线程挂到 entryList |
| **可见性** | 解锁刷主存 + 加锁 reload | 解锁：工作内存强制刷回主存（StoreLoad 屏障）；加锁：丢弃本地工作内存，强制从主存 reload |
| **有序性** | JIT 禁止指令重排 | 禁止把 synchronized 块内的指令重排到块外；CPU StoreLoad 屏障防止跨块重排 |

**happens-before 第 17 条规则**：对一个 synchronized 块的解锁，happens-before 后续对同一把锁的加锁。这是 JMM 保证可见性的契约基础。

#### 三种使用场景

| 场景 | 锁对象 | 字节码 |
|------|--------|--------|
| 修饰实例方法 | `this` | 方法常量池 `ACC_SYNCHRONIZED` 标志 |
| 修饰静态方法 | Class 对象 | 方法常量池 `ACC_SYNCHRONIZED` 标志 |
| 修饰代码块 | 指定对象 | `monitorenter` / `monitorexit` / `athrow` |

#### synchronized 锁对象深入（面试陷阱）

**核心规则**：锁的是"对象引用"，不是"类型"或"代码块"

- **非静态方法** → 锁 `this`（当前实例对象）
- **静态方法** → 锁 `Class<T>.class`（**JVM 全局单例**，所有实例共享一把锁）
- **`synchronized(obj)`** → 锁 `obj` 引用指向的对象

**Class 对象是全局单例**：
- 每个类加载后有一个唯一的 `Class` 实例
- `MyClass.class` 就是这个引用
- 因此 static synchronized 会让**所有实例**互相阻塞（即使两个不同实例）

**工程实践建议**：
- 推荐私有 final 锁对象：`private final Object lock = new Object();`
- 避免锁 `this`（外部代码能拿到）
- 避免锁常量：`synchronized("foo")` 锁常量池 String，全 JVM 共享一把锁（大坑）

#### Monitor（管程）

重量级锁的真正实现。每个对象隐含一个 Monitor，包含：
- **owner**：当前持锁线程
- **entryList**：等待获取锁的线程队列
- **waitSet**：wait() 挂起的线程集合

### STEP 4 · 简化

> **一句话总结**：synchronized 靠对象头 Mark Word 记录锁状态 + 锁升级四条路径（无锁→偏向锁→轻量级锁→重量级锁）+ 三大特性各自独立底层（Monitor 互斥 / 解锁刷主存 / JIT 禁重排）。是非公平锁，JDK 15 后偏向锁废弃。

## 常见误区

- **误区 1**：synchronized 是公平锁 → 正确：是**非公平锁**，JVM 内部不保证先到先得
- **误区 2**：有序性 = 线程顺序获取锁 → 正确：有序性指**指令重排**，不是线程调度顺序
- **误区 3**：锁升级是 无锁→轻量级锁→重量级锁 → 正确：跳过了**偏向锁**，完整路径是无锁→偏向锁→轻量级锁→重量级锁
- **误区 4**：锁可以降级 → 正确：JVM 保证锁只有升级没有降级
- **误区 5**：锁住进程 → 正确：Java 里是**锁对象/代码块**，不是锁进程

## 延伸追问

1. **synchronized 的锁能降级吗？**
   - 不能。JVM 规定锁只有升级没有降级，避免锁状态频繁切换的开销
2. **偏向锁为什么被废弃了？**
   - JDK 15 JEP 374 默认关闭，JDK 18 移除。偏向锁撤销代价高（需 STW），现代 GC 让偏向锁优化不划算
3. **synchronized 和 ReentrantLock 怎么选？**
   - 低竞争用 synchronized（JVM 内置、零开销、代码简洁）；高竞争、需要中断/超时/公平锁/多条件变量用 ReentrantLock

## 速查表（面试前 30 秒扫完）

```
对象头结构：
  Mark Word（8 字节）= 哈希码 + GC 年龄 + 锁标志位 + 锁指针
  Klass Pointer（4-8 字节）

锁升级路径：
  无锁 → 偏向锁（零开销）→ 轻量级锁（CAS 自旋）→ 重量级锁（OS 阻塞）
  只有升级没有降级
  JDK 15 关闭偏向锁，JDK 18 移除

三特性底层：
  原子性 = Monitor owner 互斥
  可见性 = 解锁刷主存 + 加锁 reload
  有序性 = JIT 禁止指令重排 + StoreLoad 屏障

关键术语：
  synchronized 是非公平锁
  锁对象/代码块（非"锁住进程"）
  有序性=指令重排（非"线程顺序获取锁"）
```

## 关联题目（题库）

- [ ] 《synchronized是怎么实现的？》— 2026-09-26 Round 1 Q6, ⭐⭐⭐
- [ ] 《synchronized是如何保证原子性、可见性、有序性的？》— 2026-09-26 Round 1 Q8, ⭐⭐

## 关联知识

- [并发与并行基础 + 线程安全](./concurrency-foundations.md)
- [JMM 与 volatile/CAS](./jmm-volatile-cas.md)
- [AQS 四大核心机制](./aqs-mechanism.md)

## Anki 候选卡片

1. **正**：synchronized 的对象头结构？**反**：Mark Word（8 字节：哈希码+GC 年龄+锁标志位+锁指针）+ Klass Pointer（4-8 字节）
2. **正**：synchronized 锁升级的四条路径？**反**：无锁→偏向锁（零开销）→轻量级锁（CAS 自旋）→重量级锁（OS 阻塞），只有升级没有降级
3. **正**：synchronized 三大特性各自底层机制？**反**：原子性=Monitor owner 互斥；可见性=解锁刷主存+加锁 reload；有序性=JIT 禁止指令重排
4. **正**：synchronized 是公平锁还是非公平锁？**反**：非公平锁，JVM 内部不保证先到先得
5. **正**：偏向锁为什么被废弃？**反**：撤销代价高需 STW，现代 GC 下不划算；JDK 15 默认关闭，JDK 18 彻底移除
6. **正**：happens-before 第 17 条规则？**反**：synchronized 块的解锁 happens-before 后续对同一把锁的加锁

---

*最后更新：2026-09-26*
