0

0

什么是Java内存模型(JMM)_主内存与工作内存的数据交互规范

P粉602998670

P粉602998670

发布时间:2026-02-12 15:11:07

|

711人浏览过

|

来源于php中文网

原创

主内存和工作内存是jmm抽象概念,非jvm堆栈别名;主内存对应线程共享变量真实值,工作内存是线程私有缓存副本,局部变量不受jmm约束。

什么是java内存模型(jmm)_主内存与工作内存的数据交互规范

主内存和工作内存不是JVM堆和栈的别名

很多人一看到“主内存”就去翻JVM内存结构图,以为它等于堆,“工作内存”等于虚拟机栈——这是最典型的误解。主内存是JMM抽象出来的共享数据源概念,对应的是所有线程可见的变量真实值(比如堆里某个对象的int count字段);工作内存则是每个线程私有的副本缓存区,它可能落在CPU寄存器、L1/L2缓存,甚至部分在栈帧里,但**不等于整个栈**。

关键区别在于:局部变量、方法参数从不进入主内存,它们天生线程私有,JMM根本不关心;而实例字段、静态字段、数组元素才受JMM规则约束。

  • 错误现象:i++ 在多线程下结果小于预期 → 因为i被各线程各自加载副本,修改后没同步回主内存
  • 实操建议:调试时别盯着javap -v看栈帧大小,要关注变量是否被volatile修饰、是否在synchronized块内、或是否用AtomicInteger
  • 容易踩的坑:把final int x = 5;当成普通变量处理——其实final字段在构造完成那一刻就对其他线程可见,这是JMM对不可变性的特殊保障,和volatile机制不同

read/load/use/assign/store/write 这8个操作不是Java语法,而是语义约束

JMM定义的这8种原子操作,不是你能写出来的代码,而是JVM执行引擎在背后必须保证的底层行为契约。比如你写counter++,JVM必须拆成至少read→load→use→assign→store→write六步,且每一步都不可分割——但问题来了:这些步骤之间没有天然顺序保证。

  • 常见错误现象:线程A执行a = 1; flag = true;,线程B看到flag == true却读到a == 0 → 这就是重排序+可见性缺失的典型组合
  • 实操建议:想让a = 1对B可见,不能只靠flag = true,得让flagvolatile,或者把两行包进synchronized
  • 参数差异:volatile仅保障单变量的可见性和禁止重排序;synchronized还能提供原子性和锁互斥,但开销大;AtomicInteger用CAS实现无锁原子更新,适合计数类场景

volatile 并不能让 i++ 变成原子操作

这是面试和线上故障里最高频的误用。给int countervolatile,确实能保证每次读都从主内存取最新值、每次写都立刻刷回主内存,但它**完全不管i++内部的三步是否被其他线程穿插执行**。

传声港
传声港

AI驱动的综合媒体服务平台,提供 “媒体发稿 + 自媒体宣发 + 效果监测” 一站式服务

下载

立即学习Java免费学习笔记(深入)”;

也就是说:volatile int i = 0;之后,两个线程同时执行i++,最终结果极大概率是1,而不是2。

  • 错误现象:volatile修饰的计数器在压测中始终少于预期 → 不是可见性问题,是原子性缺失
  • 实操建议:计数用AtomicInteger,状态开关用volatile boolean,复杂逻辑用synchronizedReentrantLock
  • 性能影响:volatile写会插入store屏障,比普通写慢;AtomicInteger.incrementAndGet()在无竞争时基本是单条CPU指令,比锁快得多

happens-before 是理解JMM的真正入口,不是可选项

你不背8种操作,也能写出正确并发代码;但如果不理解happens-before规则,就永远在猜“为什么这里有时生效有时不生效”。它才是JMM对开发者暴露的、可推理的顺序契约。

比如:一个synchronized块结束,和下一个synchronized块开始之间,存在隐式的happens-before关系——这意味着前者的写一定对后者可见。而volatile变量的写与后续任意线程对该变量的读之间,也构成happens-before

  • 容易踩的坑:在synchronized块里改了list.add(x),却在块外读list.size() → 块外读不被happens-before覆盖,可能看不到新元素
  • 实操建议:遇到“明明改了却读不到”,先检查两个操作是否落在同一个happens-before链上;优先用volatile + happens-before组合,而不是靠Thread.sleep(100)碰运气
  • 复杂点:final字段的初始化完成,与构造方法返回之间存在happens-before,所以安全发布对象时,用finalvolatile更轻量
JMM的难点从来不在记住那些术语,而在于每次写共享变量时,都要下意识问一句:这个读/写动作,有没有被某个happens-before边界定住?没有的话,就等于裸奔。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
java中boolean的用法
java中boolean的用法

在Java中,boolean是一种基本数据类型,它只有两个可能的值:true和false。boolean类型经常用于条件测试,比如进行比较或者检查某个条件是否满足。想了解更多java中boolean的相关内容,可以阅读本专题下面的文章。

358

2023.11.13

java boolean类型
java boolean类型

本专题整合了java中boolean类型相关教程,阅读专题下面的文章了解更多详细内容。

36

2025.11.30

counta和count的区别
counta和count的区别

Count函数用于计算指定范围内数字的个数,而CountA函数用于计算指定范围内非空单元格的个数。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

198

2023.11.20

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

708

2023.08.02

int占多少字节
int占多少字节

int占4个字节,意味着一个int变量可以存储范围在-2,147,483,648到2,147,483,647之间的整数值,在某些情况下也可能是2个字节或8个字节,int是一种常用的数据类型,用于表示整数,需要根据具体情况选择合适的数据类型,以确保程序的正确性和性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

559

2024.08.29

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

193

2025.08.29

C++中int的含义
C++中int的含义

本专题整合了C++中int相关内容,阅读专题下面的文章了解更多详细内容。

206

2025.08.29

c++中volatile关键字的作用
c++中volatile关键字的作用

本专题整合了c++中volatile关键字的相关内容,阅读专题下面的文章了解更多详细内容。

69

2025.10.23

2026春节习俗大全
2026春节习俗大全

本专题整合了2026春节习俗大全,阅读专题下面的文章了解更多详细内容。

189

2026.02.11

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Kotlin 教程
Kotlin 教程

共23课时 | 3.5万人学习

C# 教程
C# 教程

共94课时 | 9.3万人学习

Java 教程
Java 教程

共578课时 | 64.1万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号