JMM中的内存屏障
引言
在多线程编程中,内存可见性和指令重排序是两个关键问题,它们直接影响程序的正确性和性能。Java内存模型(JMM, Java Memory Model)通过定义一套规范,为开发者提供了在多线程环境下操作共享内存的规则。其中,内存屏障(Memory Barrier)是JMM的核心机制之一,它通过限制指令重排序和确保内存可见性,帮助开发者编写出线程安全的代码。
1. JMM层面的内存屏障
- LoadLoad屏障: 对于这样的语句Load1; LoadLoad; Load2,在Load2及后续读取操作要读取的数据被访问前,保证Load1要读取的数据被读取完毕。
- StoreStore屏障:对于这样的语句Store1; StoreStore; Store2,在Store2及后续写入操作执行前,保证Store1的写入操作对其它处理器可见。
- LoadStore屏障:对于这样的语句Load1; LoadStore; Store2,在Store2及后续写入操作执行前,保证Load1要读取的数据被读取完毕。
- StoreLoad屏障: 对于这样的语句Store1; StoreLoad; Load2,在Load2及后续所有读取操作执行前,保证Store1的写入对所有处理器可见。
JVM内存屏障依赖于底层CPU的屏障指令来实现,让我们的程序员不需要关心到系统的底层,只需要关心他们的自己的程序逻辑开发和如何使用这套规范即可,而封装这套解决方案的模型就是我们常说的Java内存模型JMM(Java Memory Model)。
1.1 JVM内存屏障的作用
- 阻止指令重排序:在插入内存屏障指令后,不管前面与后面任何指令,都不能与内存屏障指令进行重排,保证前后的指令按顺序执行。
- 全局可见:插入的内存屏障,保证了其对变量的修改结果会立即写入主内存,同时通过缓存一致性协议和总线嗅探机制,使其它CPU核心缓存该变量对应的缓存行无效,迫使其它线程下次访问该变量时,只能从主内存获取,即保证了可见性 ,解决了普通读写的延迟问题。
2. CPU读写屏障
写屏障:
- 确保对屏障之前的所有写操作的结果在继续执行屏障之后的任何指令前都是可见的。(防止指令重排)
- 在内存屏障语句之后的所有针对CPU缓存的写操作开始之前,必须先把Store Buffer(写缓冲区)中的数据全部刷新到CPU缓存中。(保证可见性)
读屏障:
- 确保对屏障之前的所有读操作的结果在继续执行屏障之后的任何指令前都是可见的。(防止指令重排)
- 在内存屏障语句之后的所有针对CPU缓存的读操作开始之前,必须先把Invalidate Queue(失效队列)中的数据全部应用到CPU缓存中。(保证可见性)
全屏障:
- 是读屏障和写屏障的组合,确保屏障之前的所有操作在屏障之后的所有操作之前完成,并保证可见性。
感谢您的阅读!如果文章中有任何问题或不足之处,欢迎及时指出,您的反馈将帮助我不断改进与完善。期待与您共同探讨技术,共同进步!