【Java】Java内存模型

article/2025/10/5 16:10:22

1.什么是Java内存模型

Java内存模型(Java Memory Model,JMM)是一种抽象的,不存在的概念。是一种屏蔽了各种硬件和操作系统的访问差异的,保证了Java程序在各种平台下对内存的访问都能保证效果一致的机制及规范。
Java内存模型具体定义了:所有的变量(包括类变量、成员变量,但是不包括方法内局部变量)都应该存储在主内存中;每个线程都有自己的工作内存空间,线程对变量(为主内存变量副本)的读取、赋值操作必须在自己的工作内存进行,不能直接对主内存中的变量值进行操作;不同线程之间无法访问对方工作内存中的变量,线程之间的变量值传递需要经过主内存。主内存、线程、以及工作内存三者之间的协同关系,如下图所示:
在这里插入图片描述
JMM 描述的是一组规则,通过这组规则控制各个变量在共享数据区域内和私有数据区域的访问方式,JMM是围绕原子性、有序性、可见性展开。与Java内存区域模型的Java堆、栈、方法区本质上不是同一层次的内存划分规则,两种维度之间没有直接对应关系。 如果非要将Java内存模型中的工作内存和主内存一定要落实到对应的Java内存区域模型中时,工作内存可以对应为栈,而主内存则可以对应为堆。

2.主内存、工作内存

主内存主要存储的是Java实例对象,所有线程创建的实例对象都存放在主内存中,不管该实例对象是成员变量还是方法中的本地变量(也称局部变量),当然也包括了共享的类信息、常量、静态变量。在主内存中的实例对象可以被多线程共享,多个线程同时调用类同一个对象的同一个方法,那么两个线程会将要操作的数据拷贝一份到直接的工作内存中,执行完操作后才刷新到主内存。由于是共享数据区域,多个线程访问同一个变量可能会引发线程安全问题。
工作内存主要存储当前方法的所有本地变量信息,是主内存中的变量副本拷贝,每个线程只能访问自己的工作内存,即线程中的本地变量对其他线程是不可见的,就算是两个线程执行的是同一段代码,它们也会在各自的工作内存中创建属于当前线程的本地变量,由于工作内存是每个线程的私有数据,线程间无法相互访问工作内存,因此存储在工作内存的数据不存在线程安全问题。需要注意的是,如果本地变量是基本数据类型,不会受到Java内存模型的访问方式限制,因为这种变量是直接存储在自己的线程栈帧的。

3.Java内存模型遵循的原则

3.1.原子性

原子性指的是一个操作不可中断,即使是在多线程环境下,一个操作一旦开始就不会被其他线程影响。
Java内存模型中定义了主内存与工作内存之间的交互方式,明确了一个变量如何从主内存拷贝到工作内存,以及如何从工作内存同步到主内存空间。这些操作细节主要由8种操作来实现,每一种实现都要求是原子性操作,主要如下所示:

  • lock(锁定):作用于主内存变量,把一个变量标识为一个线程独占状态
  • unlock(解锁):作用于主内存变量,把处于锁定状态的变量释放,释放后才可以被其他线程操作
  • read(读取):作用于主内存变量,把变量值从主从读取到工作内存,为load动作做准备
  • load(载入):作用于工作内存变量,把read操作读取到的变量值放入工作内存的变量副本中
  • use(使用):作用于工作内存变量,把该变量值传递给执行引擎(每当虚拟机指令需要使用变量时触发)
  • assign(赋值):作用于工作内存变量,把从执行引擎接收到的值赋值给该变量(每当虚拟机赋值触发动作)
  • store(存储):作用于工作内存变量,把工作内存的变量值传递给主内存,为write操作做准备
  • write(写入):作用于主内存变量,把store操作从工作内存传递过来的值放入主内存变量中

Java内存模型还定义了这些操作之间的组合性、顺序性问题。如:把一个变量从主内存拷贝到工作内存需要经过read和load操作,从工作内存拷贝到主内存需要经过store和write操作,其中read和load,store和write操作只要求顺序,不要求连续,他们执行的过程中间可以允许插入其他指令,例如:read a、read b、load b、load a。Java内存模型还规定了操作时必须满足的规则:

  • 不允许read、load、store、write操作单独出现,不允许线程丢弃最近的assign操作,在工作内存赋值后必须同步写回主内存
  • 不允许线程无原因的把数据从工作内存同步到主内存(未发生过assign操作)
  • 变量只能在主内存中诞生,不允许工作内存直接使用未初始化的变量,use、store之前必须先load、assign
  • 变量在同一时刻只允许一个线程进行lock,lock可以多次,但是unlock需要等同lock的次数
  • 变量执行lock操作,将清空工作内存该变量的值,再次进行load或assign初始化变量的值
  • 未进行lock操作的变量不允许对其进行unlock操作,也不允许unlock被其他线程锁定的变量
  • 变量进行unlock操作前必须先进行store和write操作

Java内存模型中的八种基本操作以及顺序约束,保证的是并发操作下的安全性。多线程编程时保持原子性,可以使用Java的java.util.concurrent包,它将操作简化为read、write、lock、unlock等相关API工具类;可以利用 JVM 对基本数据类型读写操作的原子性;还可以通过 synchronized 实现原子性。
另外,有一点需要说明,对于32位系统来说,long 类型数据和 double 类型数据的读写并非原子性的,也就是说如果存在两条线程同时对 long 类型或者 double 类型的数据进行读写是存在相互干扰的,因为对于32位虚拟机来说,每次原子读写是32位,而 long 和 double 则是64位的存储单元,这样导致一个线程在写时,操作完成前32位的原子操作后,轮到B线程读取时,恰好只读取来后32位的数据,这样可能回读取到一个即非原值又不是线程修改值的变量,它可能是“半个变量”的数值,即64位数据被两个线程分成了两次读取。

3.2.有序性

在执行程序时,为了提高性能,编译器和处理器常常会对指令进行重排序。保证有序性就是保证重排序后指令的执行顺序与期望一致。
Java内存模型并非在任何情况下都会进行重排序。下面几种常见情况就不会改变执行顺序:

  • Java编译器在生成指令序列的时候会禁止特定类型的处理器&编译器进行重排序的处理,并且重排序优化需遵循数据的依赖性。所谓依赖性是指如果两个操作同时访问一个变量,构成数据上下文依赖。
  • 满足先行发生原则(happens-before原则)。JVM中已经内置了一些先行发生原则:
    • 程序次序规则(Program Order Rule):在一个线程内,按照控制流顺序,书写在前面的操作先行发生于书写在后面的操作。注意,这里说的是控制流顺序而不是程序代码顺序,因为要考虑分支、循环等结构。
    • 管程锁定规则(Monitor Lock Rule):一个unlock操作先行发生于后面对同一个锁的lock操作。这里必须强调的是“同一个锁”,而“后面”是指时间上的先后。
    • volatile变量规则(Volatile Variable Rule):对一个volatile变量的写操作先行发生于后面对这个变量的读操作,这里的“后面”同样是指时间上的先后。
    • 线程启动规则(Thread Start Rule):Thread对象的start()方法先行发生于此线程的每一个动作。
    • 线程终止规则(Thread Termination Rule):线程中的所有操作都先行发生于对此线程的终止检测,我们可以通过Thread::join()方法是否结束、Thread::isAlive()的返回值等手段检测线程是否已经终止执行。
    • 线程中断规则(Thread Interruption Rule):对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过Thread::interrupted()方法检测到是否有中断发生。
    • 对象终结规则(Finalizer Rule):一个对象的初始化完成(构造函数执行结束)先行发生于它的finalize()方法的开始。
    • 传递性(Transitivity):如果操作A先行发生于操作B,操作B先行发生于操作C,那就可以得出操作A先行发生于操作C的结论。

处理器采用了指令级并行技术来将多条指令重叠执行。如果不存在数据依赖性,处理器可以改变语句对应的机器指令的执行顺序;指令重排只会保证单线程中串行语义的执行的一致性,但并不会关心多线程间的语义一致性。这时重排后将会有可能出现指令顺序错误的问题。
多线程编程时保持有序性,可以通过Java的 volatile 关键字来保证一定的“有序性”;另外可以通过 synchronized 和 final。

3.3.可见性

可见性指的是当一个线程修改了某个共享变量的值,其他线程是否能够马上得知这个修改的值。在多线程环境,由于线程对共享变量的操作都是线程拷贝到各自的工作内存进行操作后才写回到主内存中的,这就可能存在一个线程A修改了共享变量的值,还未写回主内存时,另外一个线程B又对主内存中同一个共享变量进行操作,但此时A线程工作内存中共享变量对线程B来说并不可见,这种工作内存与主内存同步延迟现象就会造成可见性问题,另外指令重排以及编译器优化也可能导致可见性问题。
Java的 volatile 关键字可以保证可见性。当一个共享变量被 volatile 关键字修饰时,它会保证修改的值立即被其他的线程看到,即修改的值立即更新到主存中,当其他线程需要读取时,它会去内存中读取新值。synchronized 也可以保证可见性。

4.参考

https://www.cnblogs.com/starsray/p/16395937.html
https://segmentfault.com/a/1190000037799975
https://www.cnblogs.com/zh94/p/14109703.html


http://chatgpt.dhexx.cn/article/hxuyXSOW.shtml

相关文章

2.什么是JAVA内存模型?

为什么要设计JAVA内存模型? 小陈:老王,看了上一篇的《CPU多级缓存模型》,有个疑问为什么还要有JAVA内存模型啊? 老王:这么来说吧,CPU多级缓存模型,只是一个规范,但是底层…

Java内存模型是什么

网上有很多关于Java内存模型的文章,在《深入理解Java虚拟机》和《Java并发编程的艺术》等书中也都有关于这个知识点的介绍。但是,很多人读完之后还是搞不清楚,甚至有的人说自己更懵了。本文,就来整体的介绍一下Java内存模型&#…

Java内存模型是什么,为什么要有Java内存模型,Java内存模型解决了什么问题等。。。

本文中,有很多定义和说法,都是笔者自己理解后定义出来的。希望能够让读者可以对Java内存模型有更加清晰的认识。当然,如有偏颇,欢迎指正。 为什么要有内存模型 在介绍Java内存模型之前,先来看一下到底什么是计算机内存…

Java内存模型相关面试题

一、谈谈你对Java内存模型的理解可以吗? 对于Java内存模型大家千万不能和JVM内存模型弄混了,不一样的; JVM内存模型是指JVM的内存分区,就是JVM分了几个区域;而Java内存模型是一种虚拟机规范。 首先来看下面一段代码&…

什么是Java内存模型?

点击上方“程序员小灰”,选择“置顶公众号” 有趣有内涵的文章第一时间送达! 本文转载自公众号 占小狼的博客 说”JVM内存模型“,有人会说是关于JVM内存分布(堆栈,方法区等)这些介绍,也有地方说…

Java面试--Java内存模型

面试题:你了解Java内存模型吗?(顺丰面试题) 面试题:程序内存的分布,五个部分(360面试题) 一、Java程序的执行过程: Java源代码文件(.java后缀)会被Java编译器编译为字节码文件(.class后缀),然后由JVM中…

JAVA内存模型与JVM内存模型

JAVA内存模型(JMM)与JVM内存模型 1、JAVA内存模型保证了在多线程环境下,对共享变量读写的原子性、可见性和有序性的一系列规范。 2、JVM内存模型规定了JAVA虚拟机在运行时使用的内存的各个分区及其作用。JAVA内存模型 原子性: 通过 synchronized、lock实现&#x…

Java内存模型(JMM)详解

目录 一、为什么要有内存模型二、CPU和缓存一致性2.1 为什么需要CPU cache2.2 三级缓存(L1、L2、L3)2.3 乱序执行优化 三、java内存模型3.1 JVM对Java内存模型的实现3.2 Java内存模型和硬件架构之间的桥接3.3 Java内存模型 - 同步八种操作3.4 Java内存模…

jvm内存模型和java内存模型

初识java虚拟机,就碰到一系列不懂的问题。我们以前常说java把局部变量放在栈里,new出来的变量放在堆里,然后堆里的数据不定时就给回收了。然后,如果是多线程的话,每个线程自己都有会一个私有的虚拟机栈,运行…

JAVA内存模型与JVM内存模型的区别

** JAVA内存模型与JVM内存模型的区别 ** 直接进入正题 **JAVA内存模型: Java内存模型规定所有的变量都是存在主存中,每个线程都有自己的工作内存。线程堆变量的操作都必须在工作内存进行,不能直接堆主存进行操作,并且每个线程…

【3】Java内存模型

目录 知识点1:Java内存模型 知识点2:Volatile 1、什么是Volatile 2、Volatile特性 3、Volatile与Synchronized区别 知识点3:重排序 1、数据依赖性 2、as-if-serial语义 3、程序顺序规则 4、重排序对多线程的影响 知识点1&#xff1a…

Java内存模型详解

文章目录 一、什么是JMM?为什么需要JMM?二、JMM 如何抽象线程和主内存之间的关系?三、Java 内存区域和 JMM 有何区别?四、happens-before原则五、总结 一、什么是JMM?为什么需要JMM? Java 是最早尝试提供内…

java内存模型概述

java内存模型 为了控制线程之间的通信,(完成底层封装) 用来屏蔽掉各种硬件和操作系统之间的内存访问差异,以实现让Java程序在各平台下都能达到一致的内存访问效果。 JMM目标:定义程序中各个变量的访问规则&#xff0c…

理解Java内存模型(JMM)

本篇的写作思路是先阐明Java内存区域划分、硬件内存架构、Java多线程的实现原理与Java内存模型的具体关系,在弄明白它们间的关系后,进一步分析Java内存模型作用以及一些实现手段 理解Java内存区域与Java内存模型 Java内存区域 Java虚拟机在运行程序时会…

Java 内存模型

Java 内存模型(Java Memory Model),简称 JMM。 JVM 中试图定义一种 JMM 来屏蔽各种硬件和操作系统的内存访问差异,以实现让 Java 程序在各种平台下都能达到一致的内存访问效果。 一、物理内存模型 物理机遇到的并发问题与虚拟机中…

Java内存模型

Java内存模型 1.内存模型概念图2.线程共享区方法区常量池运行时常量池全局字符串池class文件常量池 堆空间3.线程独占区虚拟机栈本地方法栈程序计数器 4.对象的创建 1.内存模型概念图 2.线程共享区 方法区 方法区也是线程共享区用于储存虚拟机加载的类信息(类的版…

一文看懂Java内存模型(JMM)

文章目录 Java内存模型介绍总览图Java内存模型图线程、主内存、工作内存关系图CPU缓存架构图Java内存模型与硬件内存架构的关系 什么是Java内存模型Java内存模型的意义Java内存模型规范 Java内存模型的主要结构1、程序计数器(Program Counter Register)2…

SPSS建立时间序列模型

原始数据可能并不是时间序列,因此,对这样的数据建立时间序列模型,分为以下几步: 1 生成时间序列:定义新的时间变量,并对原始数据进行平稳处理 a)定义时间变量: 选择按什么时间序列进行定义&am…

时间序列模型(ARIMA和ARMA)完整步骤详述

我于2019年发布此篇文章至今收获了许多人的指点,当时的代码的确晦涩难懂,近期有空,将代码重新整理了一遍,重新发送至此。希望能够帮助大家更好地理解。 建模步骤: 目录 数据包和版本申明 步骤一:数据准备…

机器学习之时间序列模型

一、时间序列概念 在生产和科学研究中,对某一个或一组变量x(t)进行观察测量,将在一系列时刻t1, t2, …, tn (t为自变量)按照时间次序排列,并用于解释变量和相互关系的数学表达式。在相等的时间间隔内收集到的不同时间点的数据集合我们称之为时…