Java内存模型(JMM)

JMM 的核心概念

主内存与工作内存

  • 主内存(Main Memory)是所有线程共享的内存区域,存放着所有变量的值
  • 每个线程都有自己的 工作内存(Working Memory),它是该线程的私有内存区域。线程操作共享变量时,先从主内存将变量拷贝到工作内存中,然后对工作内存中的变量进行修改,最后再将修改结果写回主内存

共享变量

  • JMM共享变量是多个线程可以访问的变量。通常是 static 变量或者实例变量。局部变量是线程私有的,不受 JMM 的影响

内存屏障(Memory Barriers)

  • 内存屏障是指 CPU 或者编译器用来保证操作顺序的一种机制。它通过禁止指令重排,确保某些操作在执行时的顺序

变量的可见性、原子性与有序性

  • 可见性:当一个线程修改了共享变量的值,其他线程能够看到这个修改
  • 原子性:对共享变量的操作要么完全成功,要么完全失败,不会中断。对于一些基本的操作(如 i++)来说,JMM 并不保证其原子性,需要通过同步手段来确保
  • 有序性:JMM 保证每个线程内的代码执行顺序,但不一定保证所有线程之间的执行顺序。为了确保线程之间操作的顺序,JMM 提供了同步机制来控制

JMM 中的关键规则

线程间的可见性保证

  • 可见性问题的核心是,当一个线程修改了共享变量,其他线程如何及时看到这个修改。JMM 的设计通过内存同步(比如锁机制、volatile 关键字、synchronized 关键字等)来确保可见性
  • volatile 关键字:声明为 volatile 的变量会直接从主内存中读取,而不是从线程的工作内存中读取。写入 volatile 变量时,JMM 会保证该写操作对其他线程可见。volatile 确保了可见性,但不能保证原子性有序性

原子性保障

  • 在 JMM 中,只有一些基本的操作(如读取和写入一个 longdouble 类型的变量)是原子的。对于复合操作(如 i++),如果不加同步,可能会出现原子性问题
  • 原子性问题的解决方法:使用 synchronizedReentrantLock 等同步机制来确保原子性

有序性保障

  • JMM 规定了每个线程内的指令执行顺序,但在不同线程之间,JMM 不保证执行顺序。为了控制执行顺序,可以使用 synchronizedvolatileLock 等手段
  • synchronized 关键字synchronized 用于确保代码块的互斥执行,并在释放锁时会刷新工作内存中的值到主内存,从而保证线程间的可见性和顺序性
  • volatile 关键字:保证了变量的写操作立即刷新到主内存,且对该变量的读操作总是直接从主内存读取,避免了线程之间的数据不一致性

JMM 的实现和底层原理

  • MESI 协议(Modified, Exclusive, Shared, Invalid),用于多核 CPU 之间缓存数据的一致性
  • 内存屏障(Memory Barrier) :用于禁止指令重排,确保特定操作的顺序执行

volatile语义

可见性(Visibility)

  • 保证:当一个线程修改了 volatile 变量的值,新值会立即被刷新到主内存;其他线程在读取该变量时,会从主内存中重新加载最新值
  • 实现机制:通过插入 Memory Barrier(内存屏障)或缓存一致性协议(如 MESI 协议)强制同步主内存和工作内存的数据

原子性(Atomicity)

  • 单变量操作:对 volatile 变量的读写操作是原子性的(例如 count++ 不会被拆分为 read+increment+write
  • 复合操作volatile 不能保证复合操作的原子性(例如 i++a = b + c),仍需借助 synchronizedAtomicInteger 等类

禁止指令重排序(Ordering)

  • 编译器优化:编译器和处理器可能会对指令进行重排序以提高性能
  • 读操作:在读取 volatile 变量前插入 Load Barrier,禁止之前的读/写操作被重排到其后
  • 写操作:在写入 volatile 变量后插入 Store Barrier,禁止之后的读/写操作被重排到其前
  • 效果:保证 volatile 变量的读写顺序符合程序逻辑

happens-before规则

程序顺序规则

  • 同一线程内,代码执行顺序与书写顺序一致(编译器和处理器可能重排指令,但需保证单线程结果不变)

监视器锁规则

  • Lock → Unlock:对同一锁的 synchronized 块,Lock 操作必在 Unlock 前发生
  • Unlock → LockUnlock 后,其他线程的 Lock 操作才能获取该锁

volatile 变量规则

  • 写 → 读:对 volatile 变量的写操作,必在后续读操作之前完成
  • 读 → 写:对 volatile 变量的读操作,必在后续写操作之前完成

线程启动规则

  • Thread.start() 必须在新建线程的任何操作之前发生

线程终止规则

  • 线程的 run() 方法结束(正常或异常退出)必在 join() 返回之前发生

中断规则

  • 对线程的 interrupt() 调用必在该线程检测到中断状态(如 isInterrupted())之前发生

对象终结规则

  • 对象的 finalize() 方法执行完必在字段被垃圾回收之前发生(注:finalize() 已废弃)

传递性规则

  • 若 A → B 且 B → C,则 A → C(可通过多条规则推导复杂顺序约束)

深入理解Java虚拟机-JAVA内存模型与线程的更多相关文章

  1. java虚拟机10.内存模型与线程

    多任务处理在现代计算机操作系统中是一项必备的功能,让计算机同时去做几件事情,不仅是因为计算机的运算能力强大了,更重要的原因是计算机的运算速度与它的存储和通信子系统速度的差距太大,大量的时间都花费在磁盘 ...

  2. Java虚拟机:内存模型详解

    版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习! 我们都知道,当虚拟机执行Java代码的时候,首先要把字节码文件加载到内存,那么这些类的信息都存放在内存中的哪个区域呢?当我们创建一个对象实 ...

  3. java虚拟机的内存模型

    一.为什么要了解java虚拟机的内存模型 java虚拟机作为java代码运行的平台,是java技术的基石.了解java虚拟机的内存模型也就变得十分必要.它能帮助我们更好的了解java代码的运行机制,更 ...

  4. Java虚拟机—Java8内存模型(整理版)

    1.概述 对于Java程序员来说,在虚拟机自动内存管理机制的帮助下,不再需要手动释放内存,不容易出现内存泄露和内存溢出问题.一旦出现内存泄露和溢出方面的问题,如果不了解虚拟机是怎样使用内存的,排查错误 ...

  5. 深入理解JAVA虚拟机(内存模型+GC算法+JVM调优)

    目录 1.Java虚拟机内存模型 1.1 程序计数器 1.2 Java虚拟机栈 局部变量 1.3 本地方法栈 1.4 Java堆 1.5 方法区(永久区.元空间) 附图 2.JVM内存分配参数 2.1 ...

  6. Java虚拟机之内存模型

    一.java并发基础 在并发编程中存在两个关键问题①线程之间如何通信 ②线程之间如何同步. 通信 通信是指线程之间以何种机制来交换信息.在命令式编程中,线程之间的通信机制有两种:共享内存和消息传递. ...

  7. JAVA虚拟机21---JAVA内存模型

    1.Amdahl定律和摩尔定律 并发处理的广泛应用是Amdahl定律代替摩尔定律成为计算机性能发展源动力的根本原因,也是人类压榨计算机运算能力的最有力武器. Amdahl定律通过系统中并行化与串行化的 ...

  8. 如何写出让java虚拟机发生内存溢出异常OutOfMemoryError的代码

    程序小白在写代码的过程中,经常会不经意间写出发生内存溢出异常的代码.很多时候这类异常如何产生的都傻傻弄不清楚,如果能故意写出让jvm发生内存溢出的代码,有时候看来也并非一件容易的事.最近通过学习< ...

  9. java虚拟机的内存机制

    我们都知道,java程序的跨平台性离不开java虚拟机,虚拟机隔绝了底层操作系统,使得java程序可以直接运行在虚拟机之上.所以,对java的学习,离不开对java虚拟机的学习与了解.下面简单整理下j ...

  10. 深入理解JVM(一)——JVM内存模型

    JVM内存模型 Java虚拟机(Java Virtual Machine=JVM)的内存空间分为五个部分,分别是: 1. 程序计数器 2. Java虚拟机栈 3. 本地方法栈 4. 堆 5. 方法区. ...

随机推荐

  1. 推荐几个不错的 Linux 服务器管理工具

    前言 选择一款好的 Linux 服务器管理工具能够极大地提高运维效率,保障业务连续性.今天大姚给大家分享3款不错的 Linux 服务器管理工具,希望可以帮助到有需要的同学. 1Panel 1Panel ...

  2. [学习笔记]最近公共祖先(LCA)之倍增算法

    1.定义 倍增法,顾名思义就是翻倍.它能够大大地优化时间复杂度.这个方法在很多算法中均有应用,例如求 LCA(最近公共祖先).(大雾) 2.框架 如下图,我们想找 \(4\) 和 \(8\) 的最近公 ...

  3. IDEA 统计代码量

    参考:链接 打开setting 找到Plugins,打开插件市场 搜索Statistic ,点击下载 重启IDEA即可 问题 idea插件安装后不显示 插件版本与idea本身版本不同导致的不兼容.解决 ...

  4. LeetCode刷题:343. 整数拆分的完全背包写法解析

    dp的含义表示:从前i个数中挑选,满足和为j的最大乘积为多少.由于是乘积所以dp初始均为1.i为2开始是因为从1开始挑选,j为2开始应为有效数字是从2开始. 进一步空间优化,应为dp[i][j]只与其 ...

  5. 使用SpongeExt快捷完成CudaSPONGE结合PySAGES的增强采样

    技术背景 在前面的一些文章中,我们介绍过关于CudaSPONGE的安装和基础使用方法,CudaSPONGE提供的Python接口,PySAGES增强采样软件的基本使用方法,还有一篇关于CudaSPON ...

  6. 混元API的加密机制与原生集成实战

    今天,我们将重点讨论在对接混元大模型时需要特别关注的几个要点.首先,最为关键的一点是,混元大模型的加密方式相比于其他大模型更为复杂和严密.在对接过程中,我们通常避免使用混元官方提供的SDK进行集成,主 ...

  7. 玩转云端 | 拥有HBlock这项“存储盘活绝技”,数据中心也能“热辣瘦身”!

    夏天马上就要到了,"瘦身"不光是特定人群的需求,也是数据中心的需求.构建轻量化.低碳化.高性价比的新型数据中心,更有效地支撑经济社会数字化转型,已成为业界主流趋势. 如何让数据中心 ...

  8. C#中根据传入的字符串字段返回实体Lambda表达式

    我们在操作实体的时候,经常会使用到Lambda表达式,如下所示就是对实体IOT_Sample的CrtTime进行操作: var ret = NestExtension.GetSort<IOT_S ...

  9. FANUC发那科机器人维护保养与故障处理分析

    发那科机器人维护保养与故障处理分析 掌握知识:掌握发那科机器人维护保养与故障处理分析 每台机器人都需要预防性保养,这样可以保证它们在生产线上保持最佳性能和实现一致性,当机器人没有进行定期的预防性保养检 ...

  10. Oracle - [03] 存储过程

    一.什么是存储过程 存储过程是一种数据库对象,是一种存储在数据库中的可执行程序,是一些经过编写.编译而存在数据库中的SQL语句集. 二.创建存储过程的语法 create or replace proc ...