内存屏障

我们看到这个词语的时候觉得很高端,但是本质很简单,就是指定==事件先后执行的顺序==


一、意义

想要了解内存屏障,需要先对JMM(java内存模型) 一定的认知。

先看一下我们自认为的内存模型结构,顺序一致性内存模型。


image
  • 内存是单一的全局内存
  • 任意时刻,只有一个线程与内存交互

通过上面的条件,可以达到多个线程读写不会造成冲突的问题,但是及时上,这样做造成的程序运行效率就大打折扣了。

接下来,看一下真实的情况。

image
  • 内存分为主内存和本地内存
  • 线程中独立持有本地内存,本地内存中拥有共享变量的副本
  • 线程可以独立操作副本中的对象
  • 副本的更新是通过JMM控制的

通过划分本地内存和主内存,提供每个线程的运行效率,不再受制于内存读写的问题。但是存在一个问题,一个共享变量的可见性问题,由于现在线程操作的都是共享变量的副本,就造成了,其他线程无法立即感知,共享的变化,这就造成了,多个线程操作同一个变量的时候,可能操作变量被覆盖的问题。这个问题就是内存屏蔽要处理的问题之一。

说完JMM 就要说一下,指令重排的问题。指令重排实际上在任何时刻都不能更改实际运行的结果,这个约束实际上只在单线程中有效,在多线程操作中,常常出现莫名其妙的问题。==指令重排和内存屏障息息相关。==

二、种类

需要牢牢记住下面的这张表格

类型 意义
Load1; LoadLoad; Load2
确保Load1加载优先于Load2及所有后续装载指令
Store1; StoreStore; Store2 确保Store1的存储(刷新到内存)先于Store2及后续的存储指令
Load1; LoadStore; Store2 确保Load1的加载优先于Store2的存储及其后续的刷新到内存的指令
Store1; StoreLoad; Load2 确保Store2存储优先于Load2及后续所有的刷新指令。(这是一个全能型的屏障,会使得屏障之前的所有的内存访问指令完成之后,才会执行后续的内存访问指令)

三、解读

1. 知识储备

  • java字节码执行流程解读

2. 解读

重排分为两个阶段:编译期重排和运行时重排, 编译期的重排永远不会改变java代码的语意, 重点是在运行时的重排,这一点很重要,虽然字节码是按照顺序来的,但是实际上不一定是按照这个顺序,但是内存屏障就可以保证运行时的顺序。

store1  
store1 

StoreStore; //让上面的存储操作store1,store2永远先于 store2,store3

store2;
store3;

同理

store0;
load0;

StoreLoad; //这个是一个全能型的屏障,必须上面完整的执行完成后,才能执行下面的部分

store1;
load0;

你可能感兴趣的:(内存屏障)