13 | Java内存模型
该思维导图由 AI 生成,仅供参考
- 深入了解
- 翻译
- 解释
- 总结
本文深入介绍了Java内存模型的重要性和相关概念。首先通过一个反常识的例子引出了在多线程环境下可能出现的数据竞争情况,以及造成这种情况的原因。随后介绍了Java内存模型中的happens-before关系,用来描述操作之间的内存可见性。文章还通过代码示例说明了如何使用volatile字段来构造跨线程的happens-before关系,从而避免数据竞争问题。此外,还涉及了Java内存模型的底层实现,锁、volatile字段、final字段与安全发布等关键概念。总的来说,本文通过深入浅出的方式介绍了Java内存模型的重要性和相关概念,对于理解多线程编程和避免数据竞争问题具有重要的指导意义。文章内容丰富,涉及底层实现和实践环节,对读者快速了解Java内存模型提供了全面的概览。
《深入拆解 Java 虚拟机》,新⼈⾸单¥59
全部留言(69)
- 最新
- 精选
- 黑崽置顶请教个问题。刚才有说不会把volatile放到寄存器,但是应该会在栈里面对吧。直接读取主内存,读写的是栈数据,然后利用堆内存和栈上数据是利用写缓存刷新同步的?
作者回复: 我可能没有在原文中讲清楚。这里指的是volatile变量不能被分配到寄存器中,但是计算还是加载到寄存器中来计算的。 所谓的分配到寄存器中,你可以理解为编译器将内存中的值缓存在寄存器中,之后一直用访问寄存器来代表对这个内存的访问的。假设我们要遍历一个数组,数组的长度是内存中的值。由于我们每次循环都要比较一次,因此编译器决定把它放在寄存器中,免得每次比较都要读一次内存。对于会更改的内存值,编译器也可以先缓存至寄存器,最后更新回内存即可。 Volatile会禁止上述优化。
2018-08-22829 - Jerry Chan置顶博客在哪里啊?
作者回复: 文末[4]所指向的链接。
2018-08-211 - 小文同学置顶老师我提个问题。一个共享对象的变量是非volatile的,那么这个变量的写入会先写到寄存器上,再写回内存吗?那么jvm是不是无论如何都不保证啥时候变量的值会写回内存。假如另一个线程加锁访问这个变量,是不是jvm也不保证它能拿到最新数据。
作者回复: 对的!如果即时编译器把那个变量放在寄存器里维护,那么另一个线程也没辙。
2018-08-2066 - 大场镇车王置顶老师 为什么volatile内存屏障不允许所有写操作之前的读操作被重排序到写操作之后?前面不是说volatile的写操作happens before对用一字段的读操作吗
作者回复: volatile字段的happens-before关系指的是在两个不同线程中,volatile的写操作 happens-before 之后对同一字段的读操作。这里有个关键字之后,指的是时间上的先后。也就是我这边写,你之后再读就一定能读得到我刚刚写的值。普通字段则没有这个保证。 屏障不允许重排序是针对即时编译器的。写后对同一字段的读,属于数据依赖,本来也不可以重排序的。
2018-08-20529 - Alex Rao置顶老师,我在一些技术文章里看到说 volatile 的变量是存在工作内存,这个工作内存是一个什么概念?
作者回复: 工作内存是JMM抽象出来的一个概念。你可以映射到实际的CPU缓存。
2018-08-2016 - 加多置顶老师,求讲解下jvm中代码如何实现的内存屏障
作者回复: 即时编译器生成的代码里会使用CPU的内存屏障指令。HotSpot采用的lock前缀的指令,lock add DWORD PTR [rsp] 0。它也会刷缓存。 至于在即时编译器里禁止重排序所使用的”内存屏障”,就是一个特殊的编译器中间表达形式节点。
2018-08-20211 - 道法自然老师你好,关于指令重排序有点不太理解,指令重排序的粒度是方法级别的,还是整个源文件级别的。文中说道,b 加了volatile后,能够保证 b=1 先于r1=b ,这个我能理解,但是如何保证不会因为指令重排导致 b=1 先于r2=a发生呢?文中虽然说了,同一个线程中,字节码顺序暗含了r2=a happen before b=1,但是文中也提到了,拥有happen-before关系的两对赋值操作之间没有数据依赖,处理器可以指令重排序。r2=a 和b=1之间没有数据依赖呀!不好意思,这块有点迷糊,老师能给详细解答下不?
作者回复: 首先,b加了volatile之后,并不能保证b=1一定先于r1=b,而是保证r1=b始终能够看到b的最新值。比如说b=1;b=2,之后在另一个CPU上执行r1=b,那么r1会被赋值为2。如果先执行r1=b,然后在另外一个CPU上执行b=1和b=2,那么r1将看到b=1之前的值。 在没有标记volatile的时候,同一线程中,r2=a和b=1存在happens before关系,但因为没有数据依赖可以重排列。一旦标记了volatile,即时编译器和CPU需要考虑到多线程happens-before关系,因此不能自由地重排序。
2018-08-2426 - Kyle我个人理解的“JAVA内存模型”应该是包括两部分的内容: 一是运行时数据区, 二是定义了一组内存访问规则。 这里其实主要讲的是其中的第二部分内容。不知道是不是可以这样总结。
作者回复: 谢谢总结!确实,本文重点讲的是内存可见性规则。 JMM的工作内存,主内存这些概念都是抽象的,对应实际体系架构中的缓存和内存。本文切掉了抽象的那部分,直接用实际的体系架构来讲解。
2018-08-2510 - 阿巍-豆夫关于Volatile, 我想问下,如果是单个cpu的系统上运行多线程的程序,是不是这个volative就没有效果了? 因为大家都使用同一个寄存器。
作者回复: 理论上,因为都使用同一套缓存,所以不需要volatile。实际实现中,对编译器不能重排列的限制还是存在的,但具体的memory barrier指令的实现是no-op。
2018-12-038 - 第9根烟问一下,内存屏障是即时编译器生成本地代码的时候产生的??那照这个意思岂不是关闭即时编译器就实现不了happen-before原则了?
作者回复: 在解释执行时,字节码之间也有内存屏障
2018-10-2426