【高薪程序员必看】万字长文拆解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的精髓!

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

相关文章
|
10月前
|
监控 Java API
现代 Java IO 高性能实践从原理到落地的高效实现路径与实战指南
本文深入解析现代Java高性能IO实践,涵盖异步非阻塞IO、操作系统优化、大文件处理、响应式网络编程与数据库访问,结合Netty、Reactor等技术落地高并发应用,助力构建高效可扩展的IO系统。
304 0
|
10月前
|
存储 缓存 安全
深入讲解 Java 并发编程核心原理与应用案例
本教程全面讲解Java并发编程,涵盖并发基础、线程安全、同步机制、并发工具类、线程池及实际应用案例,助你掌握多线程开发核心技术,提升程序性能与响应能力。
375 0
|
7月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
397 1
|
7月前
|
JSON 网络协议 安全
【Java基础】(1)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
372 1
|
8月前
|
数据采集 存储 弹性计算
高并发Java爬虫的瓶颈分析与动态线程优化方案
高并发Java爬虫的瓶颈分析与动态线程优化方案
Java 数据库 Spring
357 0
|
8月前
|
算法 Java
Java多线程编程:实现线程间数据共享机制
以上就是Java中几种主要处理多线程序列化资源以及协调各自独立运行但需相互配合以完成任务threads 的技术手段与策略。正确应用上述技术将大大增强你程序稳定性与效率同时也降低bug出现率因此深刻理解每项技术背后理论至关重要.
539 16
|
9月前
|
缓存 并行计算 安全
关于Java多线程详解
本文深入讲解Java多线程编程,涵盖基础概念、线程创建与管理、同步机制、并发工具类、线程池、线程安全集合、实战案例及常见问题解决方案,助你掌握高性能并发编程技巧,应对多线程开发中的挑战。
|
9月前
|
数据采集 存储 前端开发
Java爬虫性能优化:多线程抓取JSP动态数据实践
Java爬虫性能优化:多线程抓取JSP动态数据实践