深度剖析:synchronized 底层实现原理(JVM 视角)

在 Java 并发编程中,synchronized 是解决线程安全问题最基础、最核心的关键字。很多开发者会用它,但90% 的人都没真正理解它的底层是如何工作的

  • 为什么加了 synchronized 就能保证线程互斥?
  • 锁信息存在哪里?对象头又是什么?
  • monitorenter/monitorexit 到底做了什么?
  • JDK 1.6 之后的锁升级(偏向锁→轻量级锁→重量级锁)原理是什么?

这篇博客将从 JVM 底层、字节码、对象结构、Monitor 机制、锁升级 五个维度,彻底讲透 synchronized 的底层实现,保证通俗易懂、逻辑清晰。


一、前置知识:Java 对象内存布局

要理解 synchronized,必须先知道:Java 的锁,是存在对象头里的

一个 Java 对象在内存中分为三部分:

  1. 对象头(Header) ------ 存储锁信息
  2. 实例数据(Instance Data)
  3. 对齐填充(Padding)

对象头核心结构(关键)

对象头里最重要的是:Mark Word它存储了:

  • 对象的 HashCode
  • GC 分代年龄
  • 锁状态标志位
  • 持有锁的线程 ID
  • 指向 Monitor 的指针

Mark Word 是 synchronized 实现锁机制的物理基础


二、synchronized 底层字节码实现

我们先看最直观的:编译后的字节码

写一段最简单的同步代码块:

java 复制代码
public class SyncDemo {
    private final Object lock = new Object();

    public void test() {
        synchronized (lock) {
            // 临界区
        }
    }
}

使用 javap -c 查看字节码:

php 复制代码
0: aload_0
1: getfield
4: monitorenter   # 获取锁
5: ...             # 执行代码
18: monitorexit    # 正常释放锁
19: goto 27
22: monitorexit    # 异常释放锁

核心结论

  1. monitorenter 线程进入同步代码块时执行,尝试获取对象的 Monitor 锁

  2. monitorexit 线程退出同步代码块时执行,释放 Monitor 锁

  3. 为什么有两个 monitorexit?

    • 一个用于正常退出
    • 一个用于异常退出JVM 保证锁一定会被释放,不会锁泄漏

这就是 synchronized 自动释放锁的底层原因。


三、Monitor 机制:synchronized 的核心引擎

什么是 Monitor?

Monitor = 监视器锁 它是 JVM 内部的一个同步工具,本质是一个同步队列结构

每个对象都会关联一个 Monitor。

Monitor 内部结构

php 复制代码
Monitor {
    int owner             // 持有锁的线程
    int count             // 重入次数
    EntryList             // 阻塞等待锁的线程
    WaitSet               // 调用 wait() 后的线程
}

执行流程(最重要)

  1. 线程执行 monitorenter
  2. 检查 owner 是否为空
    • 为空 → 获取锁,owner=当前线程count=1
    • 不为空 → 检查是不是当前线程(可重入
      • 是 → count++
      • 否 → 进入 EntryList 阻塞
  3. 线程执行 monitorexit
    • count--
    • count=0 → 释放锁,唤醒 EntryList 线程

一句话总结

synchronized 的互斥性,完全由 Monitor 实现。


四、synchronized 锁升级机制(JDK 1.6 优化)

JDK 1.6 以前,synchronized重量级锁 (效率低)。JDK 1.6 引入锁升级,让锁根据竞争程度自动升级:

锁升级流程(固定顺序)

无锁 → 偏向锁 → 轻量级锁 → 重量级锁


1. 偏向锁(Biased Lock)

适用场景:只有一个线程反复获取锁

原理
  • 锁会偏向第一个获取它的线程
  • Mark Word 存储线程 ID
  • 下次该线程再获取锁,不需要 CAS,不需要竞争
  • 直接判断线程 ID 一致就通过
优点

几乎无锁开销,单线程下效率极高。

撤销

第二个线程尝试竞争锁时,偏向锁立即撤销。


2. 轻量级锁(Lightweight Lock)

适用场景:两个线程交替执行,竞争不激烈

原理
  • 线程在自己栈帧中创建锁记录(Lock Record)
  • CAS 操作 尝试将对象 Mark Word 指向锁记录
  • CAS 成功 → 获取锁
  • CAS 失败 → 自旋(spin) 重试
优点
  • 不进入内核态
  • 不阻塞线程
  • 竞争低时比重量级锁快很多
升级

自旋一定次数仍失败 → 升级为重量级锁


3. 重量级锁(Heavyweight Lock)

适用场景:多线程激烈竞争

原理
  • 依赖 Monitor
  • 未获取锁的线程直接进入 EntryList 阻塞
  • 涉及 用户态 ↔ 内核态切换,开销大
特点
  • 最稳定
  • 开销最大
  • 高竞争下唯一可靠的锁

锁升级总结

锁类型 实现机制 优点 缺点 适用场景
偏向锁 线程 ID 标记 无开销 多线程竞争会撤销 单线程
轻量级锁 CAS + 自旋 无阻塞 自旋消耗 CPU 交替执行
重量级锁 Monitor 稳定 内核切换、阻塞 高并发竞争

五、synchronized 如何保证原子性、可见性、有序性

1. 原子性

通过 Monitor 互斥 实现,同一时刻只有一个线程进入临界区。

2. 可见性

解锁前必须将修改刷新回主内存 加锁前必须从主内存重新读取

3. 有序性

  • 同步块内具有 happens-before 语义
  • 禁止指令重排

六、synchronized 的可重入性原理

synchronized可重入锁,即同一个线程可以反复获取同一把锁。

底层原理

Monitor 中有一个 count 字段:

  • 加锁:count++
  • 释放:count--
  • count=0 才真正释放锁

所以同一个线程再次进入同步块不会被自己阻塞。


七、synchronized 与 Lock 的对比(拓展)

在 Java 并发编程中,除了 synchronized,还有 Lock(如 ReentrantLock)也是常用的锁机制。很多开发者会疑惑:两者该如何选择?下面从多个维度对比,帮你快速区分。

对比维度 synchronized Lock(ReentrantLock)
锁的实现 JVM 层面,内置锁,自动释放 API 层面,手动实现,需手动释放(try-finally)
锁的释放 自动释放(正常执行完毕 / 异常) 手动释放(必须在 finally 中释放,避免锁泄露)
锁的类型 非公平锁(默认),可通过 JVM 参数设置为公平锁 可设置公平锁 / 非公平锁(构造方法参数)
锁的功能 基础锁功能,支持重入 支持重入、中断、超时、条件变量等高级功能
性能 JDK1.6 优化后,性能接近 Lock,简单场景更优 复杂场景(如超时、中断)性能更优,灵活性更高
适用场景 简单并发场景,无需高级功能 复杂并发场景(如超时获取锁、中断锁、多条件等待)

总结

简单场景(如单线程访问、少量线程竞争)用 synchronized(简单、无需手动释放);复杂场景用 Lock(灵活、支持高级功能)。


八、总结:吃透 synchronized 核心逻辑

synchronized 是 Java 并发编程的基石,底层原理可以浓缩为以下几点:

  1. 核心作用 通过互斥,保证代码的原子性、可见性、有序性,解决线程安全问题。

  2. 使用方式

    • 修饰实例方法(锁 this)
    • 修饰静态方法(锁 Class)
    • 修饰代码块(锁任意对象)
  3. 底层实现

    • 依赖 Monitor 实现互斥
    • 依赖 对象头 Mark Word 存储锁状态
    • 通过 monitorenter/monitorexit 指令获取 / 释放锁
  4. 锁升级机制无锁 → 偏向锁 → 轻量级锁 → 重量级锁根据竞争程度自动升级,大幅提升效率。

  5. 实战避坑

    • 锁对象必须用 final 修饰
    • 尽量使用同步代码块,缩小锁粒度
    • 不要混淆实例锁和类锁
    • 避免多线程交叉获取锁造成死锁
相关推荐
XiYang-DING3 小时前
【Java SE】JVM字符串常量池:位置、创建流程、对象个数与 `intern()`
java·开发语言·jvm
2301_810160953 小时前
NumPy入门:高性能科学计算的基础
jvm·数据库·python
add45a3 小时前
超越Python:下一步该学什么编程语言?
jvm·数据库·python
qwehjk20083 小时前
使用Seaborn绘制统计图形:更美更简单
jvm·数据库·python
2301_818419013 小时前
Python虚拟环境(venv)完全指南:隔离项目依赖
jvm·数据库·python
2401_873204654 小时前
Python深度学习入门:TensorFlow 2.0/Keras实战
jvm·数据库·python
坤坤藤椒牛肉面4 小时前
常见知识点总结
jvm
2301_793804694 小时前
如何从Python初学者进阶为专家?
jvm·数据库·python
m0_518019484 小时前
用Python实现自动化的Web测试(Selenium)
jvm·数据库·python