【高薪程序员必看】万字长文拆解Java并发编程!(5):深入理解JMM:Java内存模型的三大特性与volatile底层原理

简介: JMM,Java Memory Model,Java内存模型,定义了主内存,工作内存,确保Java在不同平台上的正确运行主内存Main Memory:所有线程共享的内存区域,所有的变量都存储在主存中工作内存Working Memory:每个线程拥有自己的工作内存,用于保存变量的副本.线程执行过程中先将主内存中的变量读到工作内存中,对变量进行操作之后再将变量写入主内存,jvm概念说明主内存所有线程共享的内存区域,存储原始变量(堆内存中的对象实例和静态变量)工作内存。


image.gif 编辑

Hello大家好!👋 我是摘星✨,今天给大家带来的是《深入理解JMM:Java内存模型的核心原理与高并发实战》的学习!🚀

在多线程编程中,你是否遇到过变量值莫名“消失”线程间数据不同步,甚至单例模式失效的诡异问题?💡 其实,这些问题的根源往往在于对 JMM(Java Memory Model,Java内存模型) 的理解不够深入!

在本篇内容中,我们将:

拆解JMM的核心概念——主内存 vs 工作内存,揭秘线程间数据交互的底层逻辑;

深度剖析JMM三大特性(原子性、可见性、有序性),并对比 volatilesynchronized 的适用场景;

通过经典单例模式,分析 volatile 如何用内存屏障解决指令重排序问题;

从JDK底层 解读 volatile写屏障读屏障机制,彻底搞懂它的可见性原理!

无论你是面试突击 🎯 还是高并发实战优化 ⚡,这篇文章都能让你对JMM的理解提升一个Level!📈 快跟着我一起探索吧! 🔍💻

目录

5. JMM

5.1. JMM内存定义

5.2. JMM特性

5.3. 可见性

5.4. 有序性

5.4.1. 指令重排

5.4.2. 禁止重排

5.4.3. 指令重排示例

5.5. volatile


5. JMM

5.1. JMM内存定义

JMM,Java Memory Model,Java内存模型,定义了主内存,工作内存,确保Java在不同平台上的正确运行

  • 主内存Main Memory:所有线程共享的内存区域,所有的变量都存储在主存中
  • 工作内存Working Memory:每个线程拥有自己的工作内存,用于保存变量的副本.线程执行过程中先将主内存中的变量读到工作内存中,对变量进行操作之后再将变量写入主内存,jvm

概念

说明

主内存

所有线程共享的内存区域,存储原始变量(堆内存中的对象实例和静态变量)

工作内存

每个线程私有的内存副本,存储线程操作所需的变量副本(栈内存中的局部变量和方法参数)

5.2. JMM特性

JMM的三大特性:

  • 原子性:确保操作的是不可分割的,一个线程执行一个原子操作时,其他线程无法同时执行对同一变量的操作,保证了指令不会受到线程上下文切换的影响
  • 可见性:当一个线程修改了共享变量的值后,其他线程能够立即看见修改后的变量值,保证指令不会受到CPU缓存的影响
  • 对于用volatile关键字修饰的变量,JMM保证了读操作和写操作的可见性
  • 对于没用volatile关键字修饰的变量,需要用到同步机制synchronized来保证变量的可见性
  • 有序性:程序的执行顺序必须符合开发者的预期,保证指令不会受到CPU指令并行优化的影响

特性

作用

实现方式

原子性

确保操作不可分割(如i++非原子,AtomicInteger原子)

synchronizedLockCAS(如AtomicInteger

可见性

线程修改后其他线程立即可见(解决CPU缓存不一致

volatilesynchronizedfinal(初始化后不可变)

有序性

防止指令重排序(如单例模式的双重检查锁volatile

volatile(内存屏障)、synchronized(代码块内有序)

5.3. 可见性

适用于只有一个线程修改变量值,有多个线程读取值的情况

volatile:用于修饰成员变量和静态变量,可以避免线程从自己的工作内存中查找变量的值,必须到主内存中获取变量的值,volatile操作的变量直接写到主内存中,这样就保证了线程之间的可见性,但是不能解决原子性

synchronized既可以解决线程之间的可见性问题,也可以解决原子性问题.但是synchronized操作更重量级,性能相对低

对比维度

volatile

synchronized

可见性

✅ 强制读写主内存

✅ 通过锁机制保证

原子性

❌ 不保证复合操作(如count+

✅ 保证代码块/方法内原子性

有序性

✅ 禁止指令重排序(内存屏障)

✅ 同步块内有序(as-if-serial语义)

性能

⚡ 轻量级(仅内存可见性)

⚠️ 重量级(线程阻塞/唤醒开销)

适用场景

状态标志(如boolean flag)、单例模式

多步骤复合操作(如转账

5.4. 有序性

5.4.1. 指令重排

指令重排:在不影响最终结果的前提下,对指令的执行顺序进行重排序和组合,达到指令并行的效果.

指令重排不能缩短单条指令的运行时间,但是可以变相的提高整个程序的吞吐率

5.4.2. 禁止重排

对变量加上volatile关键字可以保证该变量之前的变量不会被重排到自己的后面

5.4.3. 指令重排示例

// 无volatile时可能发生重排序,导致其他线程看到instance未初始化完成
class Singleton {
    private static volatile Singleton instance; // 需volatile禁止重排序
    private Singleton() {}
    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton(); // 非原子操作(分配内存→初始化→赋值)
                }
            }
        }
        return instance;
    }
}

image.gif

5.5. volatile

volatile的底层实现原理是内存屏障Memory Barrier

volatile修饰的变量,会在其写指令之后加入写屏障,会在其读指令之前加入读屏障

写屏障:

  1. 保证本线程内的写指令前的指令不会重排序到其后,但是并不能保证读操作排到写屏障之前
  2. 保证写指令执行完毕后将变量值同步到主内存

读屏障:

  1. 保证读指令之后的共享变量全部从主内存中读取
  2. 保证读指令之后的指令不会排在读指令之前

volatile在JDK1.5之后才生效

屏障类型

作用

写屏障

1. 阻止屏障前的写操作重排到屏障后

2. 强制刷出工作内存到主内存(写操作后)

读屏障

1. 阻止屏障后的读操作重排到屏障前

2. 强制从主内存读取最新值(读操作前)

🎉 总结与展望

经过这篇的讲解,相信你已经对 JMM(Java内存模型) 有了更深入的理解!我们不仅剖析了 主内存与工作内存 的交互机制,还深入探讨了 原子性、可见性、有序性 这三大核心特性,并通过 volatilesynchronized 的对比,掌握了不同并发场景下的最佳实践。

🔹 关键回顾

  • **volatile** 适用于轻量级可见性控制(如状态标志、单例模式),但不保证原子性。
  • **synchronized** 能同时保证可见性+原子性,但性能开销较大,适合复杂同步场景(如转账操作)。
  • 指令重排序 虽然能优化性能,但在多线程环境下可能导致线程安全问题,而 volatile内存屏障机制可以有效禁止重排。

🔹 未来学习方向

如果你想进一步深入并发编程,可以研究:

CAS(Compare-And-Swap)Atomic 原子类

✅ **ThreadLocal 的内存泄漏问题**

✅ **ReentrantLocksynchronized 的性能对比**

🚀 实践出真知!建议你动手写几个多线程Demo,亲自体验 volatilesynchronized 的区别,这样才能真正掌握JMM的精髓!

💬 欢迎在评论区交流你的学习心得或遇到的并发问题,我们一起进步!下次见!👋

目录
相关文章
|
3月前
|
设计模式 存储 SQL
【Java并发】【volatile】适合初学者体质的volatile
当你阅读dalao的框架源码的时候,你是否会见到这样一个关键字 - - - volatie,诶,你是否会好奇,为什么要加它?加了它有什么作用?
119 14
【Java并发】【volatile】适合初学者体质的volatile
|
3月前
|
存储 缓存 安全
【原理】【Java并发】【volatile】适合初学者体质的volatile原理
欢迎来到我的技术博客!我是一名热爱编程的开发者,梦想是写出高端的CRUD应用。2025年,我正在沉淀自己,博客更新速度也在加快。在这里,我会分享关于Java并发编程的深入理解,尤其是volatile关键字的底层原理。 本文将带你深入了解Java内存模型(JMM),解释volatile如何通过内存屏障和缓存一致性协议确保可见性和有序性,同时探讨其局限性及优化方案。欢迎订阅专栏《在2B工作中寻求并发是否搞错了什么》,一起探索并发编程的奥秘! 关注我,点赞、收藏、评论,跟上更新节奏,让我们共同进步!
222 8
【原理】【Java并发】【volatile】适合初学者体质的volatile原理
|
4月前
|
缓存 安全 Java
Volatile关键字与Java原子性的迷宫之旅
通过合理使用 `volatile`和原子操作,可以在提升程序性能的同时,确保程序的正确性和线程安全性。希望本文能帮助您更好地理解和应用这些并发编程中的关键概念。
92 21
|
27天前
|
Arthas 存储 算法
深入理解JVM,包含字节码文件,内存结构,垃圾回收,类的声明周期,类加载器
JVM全称是Java Virtual Machine-Java虚拟机JVM作用:本质上是一个运行在计算机上的程序,职责是运行Java字节码文件,编译为机器码交由计算机运行类的生命周期概述:类的生命周期描述了一个类加载,使用,卸载的整个过类的生命周期阶段:类的声明周期主要分为五个阶段:加载->连接->初始化->使用->卸载,其中连接中分为三个小阶段验证->准备->解析类加载器的定义:JVM提供类加载器给Java程序去获取类和接口字节码数据类加载器的作用:类加载器接受字节码文件。
188 55
|
2月前
|
Arthas 监控 Java
Arthas memory(查看 JVM 内存信息)
Arthas memory(查看 JVM 内存信息)
105 6
|
3月前
|
存储 缓存 算法
JVM简介—1.Java内存区域
本文详细介绍了Java虚拟机运行时数据区的各个方面,包括其定义、类型(如程序计数器、Java虚拟机栈、本地方法栈、Java堆、方法区和直接内存)及其作用。文中还探讨了各版本内存区域的变化、直接内存的使用、从线程角度分析Java内存区域、堆与栈的区别、对象创建步骤、对象内存布局及访问定位,并通过实例说明了常见内存溢出问题的原因和表现形式。这些内容帮助开发者深入理解Java内存管理机制,优化应用程序性能并解决潜在的内存问题。
216 29
JVM简介—1.Java内存区域
|
7月前
|
缓存 Prometheus 监控
Elasticsearch集群JVM调优设置合适的堆内存大小
Elasticsearch集群JVM调优设置合适的堆内存大小
1289 1
|
3月前
|
消息中间件 Java 应用服务中间件
JVM实战—2.JVM内存设置与对象分配流转
本文详细介绍了JVM内存管理的相关知识,包括:JVM内存划分原理、对象分配与流转、线上系统JVM内存设置、JVM参数优化、问题汇总。
106 12
JVM实战—2.JVM内存设置与对象分配流转
|
3月前
|
缓存 监控 算法
JVM简介—2.垃圾回收器和内存分配策略
本文介绍了Java垃圾回收机制的多个方面,包括垃圾回收概述、对象存活判断、引用类型介绍、垃圾收集算法、垃圾收集器设计、具体垃圾回收器详情、Stop The World现象、内存分配与回收策略、新生代配置演示、内存泄漏和溢出问题以及JDK提供的相关工具。
JVM简介—2.垃圾回收器和内存分配策略
|
4月前
|
存储 算法 Java
JVM: 内存、类与垃圾
分代收集算法将内存分为新生代和老年代,分别使用不同的垃圾回收算法。新生代对象使用复制算法,老年代对象使用标记-清除或标记-整理算法。
63 6