news 2026/4/18 14:43:22

【JVM深度解析】第25篇:volatile与synchronized深度原理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【JVM深度解析】第25篇:volatile与synchronized深度原理

摘要

volatile 和 synchronized 是 Java 并发编程中最常用的两个关键字,但它们的底层原理却大不相同。volatile 通过内存屏障保证可见性和有序性(无原子性),synchronized 通过监视器锁保证原子性、可见性和有序性。本文深入解析两者的底层实现:volatile 的写屏障/读屏障、CAS 与 volatile 的关系、以及 synchronized 的锁升级机制(偏向锁 → 轻量级锁 → 重量级锁)。理解这些,你才能真正用好并发工具,写出高效安全的多线程代码。


一、volatile 深度解析

1.1 volatile 的作用

volatile 的三大作用: ┌──────────────────────────────────────────────────────────────────┐ │ │ │ 1. 可见性(Visibility) │ │ - 每次读取都从主内存读取 │ │ - 每次写入都立即刷新到主内存 │ │ │ │ 2. 有序性(Ordering) │ │ - 防止指令重排序 │ │ - 写操作前插入 StoreStore 屏障 │ │ - 读操作后插入 LoadLoad + LoadStore 屏障 │ │ │ │ 3. 无原子性 │ │ - volatile long/counter++ 仍然不是线程安全的 │ │ │ └──────────────────────────────────────────────────────────────────┘

1.2 volatile 写屏障

// volatile 写入的语义publicclassVolatileWrite{privatevolatileintvalue;publicvoidwrite(intnewValue){value=newValue;// volatile 写}}// 等价于:publicvoidwrite(intnewValue){// StoreStore 屏障 - 确保之前的 store 都刷新到主内存__asm__volatile("sfence");value=newValue;// StoreLoad 屏障 - 确保之前的 store 对后续的 load 可见__asm__volatile("mfence");}

1.3 volatile 读屏障

// volatile 读取的语义publicclassVolatileRead{privatevolatileintvalue;publicintread(){intresult=value;// volatile 读returnresult;}}// 等价于:publicintread(){// LoadLoad 屏障 - 确保之前 load 完成__asm__volatile("lfence");intresult=value;// LoadStore 屏障 - 确保 load 之后不会发生 store 重排序// (隐含在大多数架构中)returnresult;}

1.4 volatile 的使用场景

// 正确使用 volatile 的场景// 场景 1:状态标志publicclassService{privatevolatilebooleanrunning=true;publicvoidstop(){running=false;// 其他线程立即看到变化}publicvoidrun(){while(running){// 正确:volatile 保证可见性}}}// 场景 2:单例模式(双重检查)publicclassSingleton{privatestaticvolatileSingletoninstance;publicstaticSingletongetInstance(){if(instance==null){synchronized(Singleton.class){if(instance==null){instance=newSingleton();// volatile 防止重排序}}}returninstance;}}// 场景 3:发布安全不可变对象publicclassHolder{privatestaticvolatileHolderinstance;// 注意:value 本身是不可变对象publicstaticHoldergetInstance(){if(instance==null){instance=newHolder();}returninstance;}}

二、synchronized 深度解析

2.1 synchronized 的三大特性

synchronized 保证的三大事物: ┌──────────────────────────────────────────────────────────────────┐ │ │ │ 1. 原子性(Atomicity) │ │ - 同一时刻只有一个线程能持有锁 │ │ - 其他线程阻塞等待 │ │ │ │ 2. 可见性(Visibility) │ │ - unlock 前必须刷新所有共享变量到主内存 │ │ - lock 时清空工作内存,从主内存重新加载 │ │ │ │ 3. 有序性(Ordering) │ │ - 持有锁后,保证代码块内部不发生重排序 │ │ │ └──────────────────────────────────────────────────────────────────┘

2.2 锁升级机制

┌──────────────────────────────────────────────────────────────────┐ │ synchronized 锁升级过程 │ ├──────────────────────────────────────────────────────────────────┤ │ │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ 偏向锁(Biased Lock) │ │ │ │ │ │ │ │ 场景:只有一个线程反复进入同步块 │ │ │ │ 原理:记录线程 ID,下次直接进入 │ │ │ │ 开销:无(无需 CAS) │ │ │ │ 撤销:其他线程竞争 → 批量撤销 / 偏向锁撤销 │ │ │ └──────────────────────────────────────────────────────────┘ │ │ ↓ 其他线程竞争 │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ 轻量级锁(Lightweight Lock) │ │ │ │ │ │ │ │ 场景:少量线程交替进入同步块 │ │ │ │ 原理:CAS 替换 Mark Word,竞争失败自旋等待 │ │ │ │ 开销:自旋消耗 CPU │ │ │ │ 升级:自旋超过阈值 → 膨胀为重量级锁 │ │ │ └──────────────────────────────────────────────────────────┘ │ │ ↓ 竞争激烈 │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ 重量级锁(Heavyweight Lock) │ │ │ │ │ │ │ │ 场景:多线程竞争同一把锁 │ │ │ │ 原理:OS Mutex,线程阻塞挂起 │ │ │ │ 开销:上下文切换(毫秒级) │ │ │ │ 特点:不再自旋,释放时唤醒等待线程 │ │ │ └──────────────────────────────────────────────────────────┘ │ │ │ └──────────────────────────────────────────────────────────────────┘

2.3 Mark Word 与锁状态

Mark Word 的 64 位结构(无锁状态): ┌──────────────────────────────────────────────────────────────────┐ │ │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ 25 bit │ 31 bit │ 1 bit │ 4 bit │ 1 bit │ │ │ │ unused │ 对象哈希码 │ GC age │ 偏向锁 │ 偏向 │ │ │ │ │ (hashcode) │ │ epoch │ 标志 │ │ │ └──────────────────────────────────────────────────────────┘ │ │ │ │ 偏向锁状态: │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ 54 bit │ 2 bit │ 1 bit │ 1 bit │ │ │ │ 线程指针(Thread ID) │ epoch │ 偏向锁 │ 偏向 │ │ │ │ │ │ 类型 │ 标志 │ │ │ └──────────────────────────────────────────────────────────┘ │ │ │ │ 轻量级锁状态: │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ 62 bit │ │ │ │ 指向栈中锁记录的指针 │ │ │ │ │ │ │ └──────────────────────────────────────────────────────────┘ │ │ │ │ 重量级锁状态: │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ 62 bit │ │ │ │ 指向互斥量(Mutex)的指针 │ │ │ │ │ │ │ └──────────────────────────────────────────────────────────┘ │ │ │ └──────────────────────────────────────────────────────────────────┘

2.4 synchronized 实战

// synchronized 最佳实践// 1. 锁范围最小化publicclassMinLock{privateintcounter=0;// 好:只锁必要部分publicvoidgoodIncrement(){// 前置逻辑(不需要锁)preLogic();synchronized(this){counter++;// 只锁核心操作}// 后置逻辑(不需要锁)postLogic();}// 不好:整个方法加锁publicsynchronizedvoidbadIncrement(){preLogic();// 不需要锁的操作也被锁住了counter++;postLogic();}}// 2. 避免锁对象变化publicclassLockObjectProblem{privateStringlock="lock";// 不好:字符串常量池共享publicvoiddoSomething(){synchronized(lock){// 可能有其他代码也用同样的字符串// ...}}}// 3. 使用专门的锁对象publicclassLockObjectGood{privatefinalObjectlock=newObject();// 好:私有专用对象publicvoiddoSomething(){synchronized(lock){// ...}}}// 4. 读写分离publicclassReadWriteLock{privatefinalReadWriteLockrwLock=newReentrantReadWriteLock();privatevolatileintvalue;publicintread(){rwLock.readLock().lock();try{returnvalue;}finally{rwLock.readLock().unlock();}}publicvoidwrite(intnewValue){rwLock.writeLock().lock();try{value=newValue;}finally{rwLock.writeLock().unlock();}}}

三、volatile vs synchronized

┌──────────────────────────────────────────────────────────────────┐ │ volatile vs synchronized 对比 │ ├──────────────────────────────────────────────────────────────────┤ │ │ │ 特性 │ volatile │ synchronized │ │ ─────────────────┼────────────────────┼───────────────────── │ │ 原子性 │ 无 │ 有 │ │ 可见性 │ 有 │ 有 │ │ 有序性 │ 有(部分) │ 有(完全) │ │ 性能 │ 轻量(读屏障) │ 较重(系统调用) │ │ 使用场景 │ 状态标志/单例 │ 复杂同步逻辑 │ │ 底层实现 │ 内存屏障 │ Monitor Enter/Exit │ │ │ └──────────────────────────────────────────────────────────────────┘

四、总结

volatile 通过内存屏障保证可见性和有序性,适合状态标志等简单场景;synchronized 通过监视器锁保证三大事物,但有偏向锁→轻量级锁→重量级锁的升级机制。理解两者的底层原理,才能在并发编程中做出正确的选择。


系列导航

  • 上一篇:【JVM深度解析】第24篇:JVM内存模型(JMM)核心原理
  • 下一篇:【JVM深度解析】第26篇:CAS、AQS与并发工具类原理
  • 系列目录:JVM深度解析

参考资料

  1. JVM Lock Synchronization - Oracle
  2. Biased Locking in HotSpot
  3. Java Volatile Keyword
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/18 14:41:58

WIZnet网络芯片实战:从硬件连接到Socket编程的避坑指南

1. WIZnet网络芯片入门:硬件连接与基础调试 第一次接触W5500这类网络协议芯片时,我踩过不少坑。记得当时为了给配电终端加装以太网功能,连续三天卡在物理层连接问题上。后来才发现,这类芯片的硬件设计有自己的一套规则。 硬件复位…

作者头像 李华
网站建设 2026/4/18 14:38:46

ue5样条线生成模型

先创建需要生成的模型蓝图 加个box 碰撞体box包围木头先设置木头为不可见的状态然后给木头设置一个出现的动画利用timeline再写一个自定义 事件显示木头再新建一个actor 里面添加一个box 使它 接触的时候产生木头出现的动画 触发模型显示跟动画最后新建样条线蓝图 add chi…

作者头像 李华
网站建设 2026/4/18 14:37:47

SurveyKing私有化部署实战指南:构建企业级问卷与考试系统

SurveyKing私有化部署实战指南:构建企业级问卷与考试系统 【免费下载链接】SurveyKing One command to deploy a more powerful, self‑hosted alternative to SurveyMonkey. 项目地址: https://gitcode.com/gh_mirrors/su/SurveyKing SurveyKing是一款功能强…

作者头像 李华
网站建设 2026/4/18 14:35:57

【仅限前500名开发者】SITS2026独家披露:基于AST+Control Flow Graph的混合生成架构,让告警具备可解释性与可审计性

第一章:SITS2026演讲:AI代码告警生成 2026奇点智能技术大会(https://ml-summit.org) 在SITS2026主会场,来自CodeGuard AI实验室的研究团队首次公开演示了新一代AI驱动的实时代码告警生成系统——AlertGen v3。该系统不再依赖传统静态分析规…

作者头像 李华
网站建设 2026/4/18 14:35:52

Omni-Vision Sanctuary 模型推理加速实践:利用 .accelerate 库优化生成速度

Omni-Vision Sanctuary 模型推理加速实践:利用 .accelerate 库优化生成速度 1. 引言 如果你正在使用Omni-Vision Sanctuary这类大型图像生成模型,可能已经注意到推理速度有时会成为瓶颈。特别是在需要批量生成高质量图像时,等待时间可能会让…

作者头像 李华