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. C# webapi 允许跨域(.NET Framework)

    实际项目中,对于WebApi的访问不一定都在同一域名下,所以进行跨域访问的时候,可能会出现如下提示:请求的资源不支持 http 方法"OPTIONS".需要对WebApi进行设置. ...

  2. RELIC库学习

    <RELIC库学习> 文章介绍:密码学与区块链技术实验室向开源项目RELIC贡献国密算法代码 了解 RELIC是由Diego F. Aranha开发的高效.灵活的开源密码原语工具箱,包含多 ...

  3. Educated PG walkthrough Intermediate

    nmap 扫 到 80 22 dirsearch 扫描发现 ┌──(root㉿kali)-[~] └─# dirsearch -u http://192.168.167.13/ /usr/lib/py ...

  4. CV高手是怎么炼成的?

    你平时都怎么复制粘贴的?是否每次都是复制一段粘贴一段?是否厌倦了每次只能复制粘贴一次的限制?那这篇文章就是为你量身订做的. CopyQ简介 CopyQ is clipboard manager – a ...

  5. uni-app路由跳转

    navigateTo redirectTo (1)保留当前页面,跳转到应用内的某个页面,使用uni.navigateBack可以返回到原页面 (如果A->B来回频繁切换,不要A B两个方法都使用 ...

  6. 边缘计算与MEC浅谈

    本文分享自天翼云开发者社区<边缘计算与MEC浅谈>,作者:y****n 一.什么是边缘计算 边缘计算是在靠近物或数据源头的网络边缘侧,通过融合网络.计算.存储.应用核心能力的分布式开放平台 ...

  7. 什么是OpenStack?

    OpenStack是一个云平台管理的项目,它不是一个软件,它是由几个主要的组件组合起来,为公有云.私有云和混合云的建设与管理提供软件的开源项目.现在已经有来自100多个国家的数万名个人和200多家企业 ...

  8. kubernetes 集群中部署 nginx 服务

    kubernetes 部署nginx服务 本章节将介绍如何在kubernetes集群中部署一个nginx服务,并且能够对其进行访问. Namespace Namespace是kubernetes系统中 ...

  9. min-max 容斥(最值反演)学习笔记

    min-max 容斥,又名最值反演(我其实更喜欢后面这个名字),是一种常用的反演思想. 引入 在皇后游戏一题中,我们曾经证明过 \(\max(a,b)-a-b=-\min(a,b)\). 我们尝试推广 ...

  10. SpringBoot+Mybatis-Plus使用多数据源

    常见的使用Mybatis-Plus配置多数据源方式有两种:一种是通过java config的方式手动配置两个数据源,另一种方式便是使用 dynamic-datasource-spring-boot-s ...