我们看到这个词语的时候觉得很高端,但是本质很简单,就是指定==事件先后执行的顺序==
一、意义
想要了解内存屏障,需要先对JMM(java内存模型) 一定的认知。
先看一下我们自认为的内存模型结构,顺序一致性内存模型。
- 内存是单一的全局内存
- 任意时刻,只有一个线程与内存交互
通过上面的条件,可以达到多个线程读写不会造成冲突的问题,但是及时上,这样做造成的程序运行效率就大打折扣了。
接下来,看一下真实的情况。
- 内存分为主内存和本地内存
- 线程中独立持有本地内存,本地内存中拥有共享变量的副本
- 线程可以独立操作副本中的对象
- 副本的更新是通过JMM控制的
通过划分本地内存和主内存,提供每个线程的运行效率,不再受制于内存读写的问题。但是存在一个问题,一个共享变量的可见性问题,由于现在线程操作的都是共享变量的副本,就造成了,其他线程无法立即感知,共享的变化,这就造成了,多个线程操作同一个变量的时候,可能操作变量被覆盖的问题。这个问题就是内存屏蔽要处理的问题之一。
说完JMM 就要说一下,指令重排的问题。指令重排实际上在任何时刻都不能更改实际运行的结果,这个约束实际上只在单线程中有效,在多线程操作中,常常出现莫名其妙的问题。==指令重排和内存屏障息息相关。==
二、种类
需要牢牢记住下面的这张表格
类型 | 意义 |
---|---|
<div style="width: 150pt">Load1; LoadLoad; Load2<div> | 确保Load1加载优先于Load2及所有后续装载指令 |
Store1; StoreStore; Store2 | 确保Store1的存储(刷新到内存)先于Store2及后续的存储指令 |
Load1; LoadStore; Store2 | 确保Load1的加载优先于Store2的存储及其后续的刷新到内存的指令 |
Store1; StoreLoad; Load2 | 确保Store2存储优先于Load2及后续所有的刷新指令。(这是一个全能型的屏障,会使得屏障之前的所有的内存访问指令完成之后,才会执行后续的内存访问指令) |
三、解读
1. 知识储备
2. 解读
重排分为两个阶段:编译期重排和运行时重排, 编译期的重排永远不会改变java代码的语意, 重点是在运行时的重排,这一点很重要,虽然字节码是按照顺序来的,但是实际上不一定是按照这个顺序,但是内存屏障就可以保证运行时的顺序。
store1
store1
StoreStore; //让上面的存储操作store1,store2永远先于 store2,store3
store2;
store3;
同理
store0;
load0;
StoreLoad; //这个是一个全能型的屏障,必须上面完整的执行完成后,才能执行下面的部分
store1;
load0;