【高薪程序员必看】万字长文拆解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三大特性(原子性、可见性、有序性),并对比 volatile 和 synchronized 的适用场景;

✅ 通过经典单例模式,分析 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原子)

synchronized、Lock、CAS(如AtomicInteger)

可见性

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

volatile、synchronized、final(初始化后不可变)

有序性

防止指令重排序(如单例模式的双重检查锁需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内存模型) 有了更深入的理解!我们不仅剖析了 主内存与工作内存 的交互机制,还深入探讨了 原子性、可见性、有序性 这三大核心特性,并通过 volatile 和 synchronized 的对比,掌握了不同并发场景下的最佳实践。

🔹 关键回顾:

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

🔹 未来学习方向:

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

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

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

✅ **ReentrantLock 与 synchronized 的性能对比**

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

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

相关文章
|
机器学习/深度学习 存储 算法
NoProp:无需反向传播,基于去噪原理的非全局梯度传播神经网络训练,可大幅降低内存消耗
反向传播算法虽是深度学习基石,但面临内存消耗大和并行扩展受限的问题。近期,牛津大学等机构提出NoProp方法,通过扩散模型概念,将训练重塑为分层去噪任务,无需全局前向或反向传播。NoProp包含三种变体(DT、CT、FM),具备低内存占用与高效训练优势,在CIFAR-10等数据集上达到与传统方法相当的性能。其层间解耦特性支持分布式并行训练,为无梯度深度学习提供了新方向。
1075 1
NoProp:无需反向传播,基于去噪原理的非全局梯度传播神经网络训练,可大幅降低内存消耗
|
设计模式 缓存 Java
【JUC】(4)从JMM内存模型的角度来分析CAS并发性问题
本篇文章将从JMM内存模型的角度来分析CAS并发性问题; 内容包含:介绍JMM、CAS、balking犹豫模式、二次检查锁、指令重排问题
361 1
|
设计模式 存储 SQL
【Java并发】【volatile】适合初学者体质的volatile
当你阅读dalao的框架源码的时候,你是否会见到这样一个关键字 - - - volatie,诶,你是否会好奇,为什么要加它?加了它有什么作用?
461 14
【Java并发】【volatile】适合初学者体质的volatile
|
存储 缓存 安全
【原理】【Java并发】【volatile】适合初学者体质的volatile原理
欢迎来到我的技术博客!我是一名热爱编程的开发者,梦想是写出高端的CRUD应用。2025年,我正在沉淀自己,博客更新速度也在加快。在这里,我会分享关于Java并发编程的深入理解,尤其是volatile关键字的底层原理。 本文将带你深入了解Java内存模型(JMM),解释volatile如何通过内存屏障和缓存一致性协议确保可见性和有序性,同时探讨其局限性及优化方案。欢迎订阅专栏《在2B工作中寻求并发是否搞错了什么》,一起探索并发编程的奥秘! 关注我,点赞、收藏、评论,跟上更新节奏,让我们共同进步!
662 8
【原理】【Java并发】【volatile】适合初学者体质的volatile原理
|
缓存 安全 Java
Volatile关键字与Java原子性的迷宫之旅
通过合理使用 `volatile`和原子操作,可以在提升程序性能的同时,确保程序的正确性和线程安全性。希望本文能帮助您更好地理解和应用这些并发编程中的关键概念。
509 21
|
安全 C语言 C++
彻底摘明白 C++ 的动态内存分配原理
大家好,我是V哥。C++的动态内存分配允许程序在运行时请求和释放内存,主要通过`new`/`delete`(用于对象)及`malloc`/`calloc`/`realloc`/`free`(继承自C语言)实现。`new`分配并初始化对象内存,`delete`释放并调用析构函数;而`malloc`等函数仅处理裸内存,不涉及构造与析构。掌握这些可有效管理内存,避免泄漏和悬空指针问题。智能指针如`std::unique_ptr`和`std::shared_ptr`能自动管理内存,确保异常安全。关注威哥爱编程,了解更多全栈开发技巧。 先赞再看后评论,腰缠万贯财进门。
833 0
|
存储
阿里云轻量应用服务器收费标准价格表:200Mbps带宽、CPU内存及存储配置详解
阿里云香港轻量应用服务器,200Mbps带宽,免备案,支持多IP及国际线路,月租25元起,年付享8.5折优惠,适用于网站、应用等多种场景。
4006 0
|
存储 缓存 NoSQL
内存管理基础:数据结构的存储方式
数据结构在内存中的存储方式主要包括连续存储、链式存储、索引存储和散列存储。连续存储如数组,数据元素按顺序连续存放,访问速度快但扩展性差;链式存储如链表,通过指针连接分散的节点,便于插入删除但访问效率低;索引存储通过索引表提高查找效率,常用于数据库系统;散列存储如哈希表,通过哈希函数实现快速存取,但需处理冲突。不同场景下应根据访问模式、数据规模和操作频率选择合适的存储结构,甚至结合多种方式以达到最优性能。掌握这些存储机制是构建高效程序和理解高级数据结构的基础。
1322 1
|
存储 编译器 C语言
【C语言篇】数据在内存中的存储(超详细)
浮点数就采⽤下⾯的规则表⽰,即指数E的真实值加上127(或1023),再将有效数字M去掉整数部分的1。
1282 0

热门文章

最新文章