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. 利用mybatis拦截器记录sql,辅助我们建立索引(二)

    背景 上一篇中讲述了mybatis的mapper初始化过程和执行过程,这篇再讲讲具体的拦截器的使用,以实现记录sql到持久化存储,通过分析这些sql,我们就能更方便地建立索引. 利用mybatis拦截 ...

  2. Golang sync.pool源码解析

    Golang sync.pool源码解析 - sync.pool - 是什么 - 怎么用 - demo - 真实世界的使用 - 源码解读-数据结构 - 源码解读-读写流程 - 写流程 - 读流程 - ...

  3. Java连接数据库 CreateStatement 和 PrepareStatement 的区别与优劣

    一.简介 先说下CreateStatement 和 PrepareStatement 这俩到底是干啥的吧. 作用:其实这俩干的活儿都一样,就是创建了一个对象然后去通过对象调用executeQuery方 ...

  4. 面试官:说说你项目中JWT的执行流程?

    JWT 在目前的项目开发中使用到的频率是非常高的,因此它也是面试常问的一类问题,所以今天我们就来看看"项目中 JWT 的执行流程?"这个问题. 1.什么是 JWT? JWT(JSO ...

  5. nginx: [error] open() “/usr/local/var/run/nginx.pid” failed (2: No such file or directory)

  6. JUC并发—13.Future模式和异步编程简介

    大纲 1.Runnable接口与Callable接口 (1)Runnable接口实现异步任务 (2)Callable接口实现异步任务 2.Future模式 (1)Future模式的概念 (2)Futu ...

  7. Spark - [03] 资源调度模式

    题记部分 一.Local模式 1.1.概述 Local模式就是运行在一台计算机上的模式,通常就是用于在本机上练手和测试的. 可以通过以下几种方式设置Master (1)local:所欲计算都运行在一个 ...

  8. JavaGUI - [03] LayoutManager布局管理器

      Component中有一个方法setBounds()可以设置当前容器的位置和大小,但如果我们手动为组件设置位置和大小的话,就会造成程序的不通用性.LayoutManager布局管理器可以根据运行平 ...

  9. DeepSeek 全套资料pdf合集免费下载(持续更新)

    有很多朋友都关注DeepSeek相关使用的教程资料,本站也一直持续分享DeepSeek 学习相关的pdf资料,由于比较零散,这篇文章主要就是做一个汇总,并且持续更新,让大家可以及时获取下载最新的相关D ...

  10. 如何使用ISqlSugarClient进行数据访问,并实现了统一的批量依赖注入

    仓储层当前有接口 IRepository<T> 抽象类  BaseRepository<T> 业务逻辑层有抽象类 BaseBusiness<M, E> 接口 IBu ...