Java 内存屏障 详解Java中的内存屏障
青年it男 人气:0为什么要有内存屏障
这个是为了解决因为cpu,高速缓存,主内存出现的时候,导致的可见性和重序性问题,什么问题呢,看下面
我们都知道计算机运算任务需要CPU和内存相互配合共同完成,其中CPU负责逻辑计算,内存负责数据存储。CPU要与内存进行交互,如读取运算数据、存储运算结果等。由于内存和CPU的计算速度有几个数量级的差距,为了提高CPU的利用率,现代处理器结构都加入了一层读写速度尽可能接近CPU运算速度的高速缓存来作为内存与CPU之间的缓冲:将运算需要使用的数据复制到缓存中,让CPU运算可以快速进行,计算结束后再将计算结果从缓存同步到主内存中,这样处理器就无须等待缓慢的内存读写了。就像下面这样
高速缓存的引入解决了CPU和内存之间速度的矛盾,但是在多CPU系统中也带来了新的问题:可见性问题和重排序问题。
首先是可见性问题:假设有两个线程A、B分别在两个不同的CPU上运行,它们共享同一个变量X。如果线程A对X进行修改后,并没有将X更新后的结果同步到主内存,则变量X的修改对B线程是不可见的。这样就会造成可见性问题
然后是重排序问题:假设A、B两个线程共享两个变量X、Y,A和B分别在不同的CPU上运行。在A中先更改变量X的值放到高速缓存区,然后再更改变量Y的值放到高速缓存区。这时有可能发生Y的值被同步回主内存,而X的值没有同步回主内存的情况,此时对于B线程来说是无法感知到X变量被修改的,或者可以认为对于B线程来说,Y变量的修改被重排序到了X变量修改的前面。
就是为了解决上面的多线程里面的可见性和重序性问题,所以有了下面的内存屏障技术
内存屏障的主要组成
首先是硬件上面的内存屏障
- Load屏障,是x86上的”ifence“指令,在其他指令前插入ifence指令,可以让高速缓存中的数据失效,强制当前线程从主内存里面加载数据
- Store屏障,是x86的”sfence“指令,在其他指令后插入sfence指令,能让当前线程写入高速缓存中的最新数据更新写入主内存,让其他线程可见。
Java里面的内存屏障
在java里面有4种,就是 LoadLoad,StoreStore,LoadStore,StoreLoad,实际上也能看出来,这四种都是上面的两种的组合产生的
LoadLoad屏障:
举例语句是Load1; LoadLoad; Load2(这句里面的LoadLoad里面的第一个Load对应Load1加载代码,然后LoadLoad里面的第二个Load对应Load2加载代码),此时的意思就是在Load2加载代码在要读取的数据之前,保证Load1加载代码要从主内存里面读取的数据读取完毕。
StoreStore屏障:
举例语句是 Store1; StoreStore; Store2(这句里面的StoreStore里面的第一个Store对应Store1存储代码,然后StoreStore里面的第二个Store对应Store2存储代码)。此时的意思就是在Store2存储代码进行写入操作执行前,保证Store1的写入操作已经把数据写入到主内存里面,确认Store1的写入操作对其它处理器可见。
LoadStore屏障:
举例语句是 Load1; LoadStore; Store2(这句里面的LoadStore里面的Load对应Load1加载代码,然后LoadStore里面的Store对应Store2存储代码),此时的意思就是在Store2存储代码进行写入操作执行前,保证Load1加载代码要从主内存里面读取的数据读取完毕。
举例语句是 Load1; LoadStore; Store2(这句里面的LoadStore里面的Load对应Load1加载代码,然后LoadStore里面的Store对应Store2存储代码),此时的意思就是在Store2存储代码进行写入操作执行前,保证Load1加载代码要从主内存里面读取的数据读取完毕。
StoreLoad屏障:
举例语句是Store1; StoreLoad; Load2(这句里面的StoreLoad里面的Store对应Store1存储代码,然后StoreLoad里面的Load对应Load2加载代码),在Load2加载代码在从主内存里面读取的数据之前,保证Store1的写入操作已经把数据写入到主内存里面,确认Store1的写入操作对其它处理器可见。
Volatile关键字里面的内存屏障是起作用的
在每个volatile写操作前插入StoreStore屏障,这样就能让其他线程修改A变量后,把修改的值对当前线程可见,在写操作后插入StoreLoad屏障,这样就能让其他线程获取A变量的时候,能够获取到已经被当前线程修改的值
在每个volatile读操作前插入LoadLoad屏障,这样就能让当前线程获取A变量的时候,保证其他线程也都能获取到相同的值,这样所有的线程读取的数据就一样了,在读操作后插入LoadStore屏障;这样就能让当前线程在其他线程修改A变量的值之前,获取到主内存里面A变量的的值。
加载全部内容