这是我参与更文挑战的第28天,活动详情查看: 更文挑战。
紧接着上一篇你好,请谈谈volatile关键字?(三)
4.5 内存屏障(memory barrier)
上文讲到指令重排序会带来可见性的问题,解决的方式是使用内存屏障。什么是内存屏障?
内存屏障是一组处理器指令(硬件相关),用于对内存操作顺序做一些限制。可以用来实现多线程访问内存上同一资源的可见性。
4.5.1 借助Hsdis工具分析内存屏障
Hsdis
是一个反汇编的库,用在Java
运行时分析JIT
编译器生成的代码。它是一个dll(动态链接库)
文件,放在${JAVA_HOME}/jre/bin/server
目录下使用。我们借助这个工具对比变量前加volatile
关键字和不加关键字的汇编指令的差异。
首先在Java
安装目录配置Hsdis
环境,其实就是把两个文件放到指定目录。
在配置好Hsdis
环境后,在运行程序的VM Options
加入以下参数。
-server -Xcomp -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:CompileCommand=compileonly,*VolatileExample.*
复制代码
-XX:+UnlockDiagnosticVMOptions
参数代表开启诊断模式,以方便打印PrintAssembly
一类的信息。-XX:+PrintAssembly
参数代表打印即时编译的二进制信息XX:CompileCommand=compileonly,*VolatileExample.*
这个表示过滤仅显示满足*VolatileExample.*正则表达式的信息。
然后我们运行第二章节的例子VolatileExample。
发现加volatile
修饰的变量run
汇编指令为
0x0000000003565073: lock add dword ptr [rsp],0h ;*putstatic run
复制代码
未加volatile
修饰的变量run
汇编指令为
0x00000000030e5ceb: push 0ffffffffc4834801h ;*putstatic run
复制代码
可以很清晰的看到差异,有volatile
修饰的变量,多了一个lock
指令。lock
是一个汇编控制指令,这个指令相当于实现了一种内存屏障。
4.5.2 详解内存屏障
对于Java
语言,内存屏障不是直接由JVM
暴露出来提供使用的,而是由JVM
根据代码语义,插入到底层运行指令中的。比如上文实践分析出,加入volatile
关键字修饰变量,汇编指令上就会多出lock
指令。
不同硬件暴露出来供外界使用内存屏障指令是不一样的。比如我们熟知的X86
计算机设备,它提供了Ifence(读屏障)
、Sfence(写屏障)
、mfence(全屏障)
这几种执行内存屏障的指令。
- 读屏障(Load Memory Barrier)就是在读屏障之后的读操作,都在读屏障之后执行。这个指令要配合写屏障一起完成,达到的效果是,写屏障之前的内存更新在读屏障之后的读操作是可见的。其实质是,在读屏障之前,先应用所有已经在失效队列中的失效操作的指令。
- 写屏障(Store Memory Barrier)就是在写屏障之前的写操作,都必须要由
Store Buffer
同步刷新到主存中。达到的效果是,写屏障之后的读写操作都能看到写屏障之前的内存更新。 - 全屏障(Full Memory Barrier)就是在全屏障之前的读写操作,其产生的内存的更新,对该屏障之后的读写操作都可见。
所以对于4.4.3章节的例子,我们简单修改一下,加入内存屏障,实现可见性。
int value =1;
bool finish = false;
void runOnCPU1(){
value = 2;
storeMemoryBarrier();//伪代码,写屏障,强制变量刷新到主存
finish = true;
}
void runOnCPU2(){
if(finish){
loadMemoryBarrier();//伪代码,读屏障,获取多线程环境下最新的变量值
assert value == 2;
}
}
复制代码
所以总结下来,Java
编译器在 volatile 字段的读写操作前后各插入一些内存屏障,解决了指令重排序的问题。这里要特别提到的是,对于Java
语言,解决指令重排序的真正的功臣是JMM(Java Memory Model)
。
JMM
作为沟通复杂硬件底层实现的桥梁,通过提供了一些合理的禁用缓存以及禁止重排序的方法,然后在编译时转换成具体的CPU
指令,解决了可见性
和有序性
问题,具体的方法包括synchronized
、volatile
、final
。这一章节,不做深入,后续进行详细分析。
下一篇,终章,总结volatile,敬请期待