内存屏障与JVM并发 - 编程入门网
之间创建了一个先后关系。 编译器无法重新排序这些写操作,如果必要,它会利用一个内存屏障禁止处理器 重排序。让我们来看看一些实现细节。
PrintAssembly HotSpot选项是JVM的一个诊断标志,允许我们获取JIT编译器 生成的汇编指令。这需要最新的OpenJDK版本或者新HotSpot update14或者更高版 本。通过需要一个反编译插件。Kenai项目提供了用于Solaris、Linux和BSD的插 件二进制文件。hsdis是另一款可以在Windows通过源码构建的插件。 两次顺序读操作的第一次(第三行)的汇编指令如下。指令流基于Itanium 2 多处理硬件、JDK 1.6 update 17。本文的所有指令流都在左手边以行号标记。相 关的读操作、写操作和内存屏障指令都以粗体标记。建议读者不要沉迷于每一行 指令。
简短的指令流其实内容丰富。第一次volatile位于第二行。Java内存模型确保 了JVM会在第二次读操作之前将第一次读操作交给处理器,也就是按照 “程序的 顺序”——但是这单单一行指令是不够的,因为处理器仍然可以自由乱序执行这 些操作。为了支持Java内存模型的一致性,JVM在第一次读操作上添加了注解 ld.acq,也就是“载入获取”(load acquire)。通过使用ld.acq,编译器确保 第二行的读操作在接下来的读操作之前完成。问题就解决了。 请注意这影响了读操作,而不是写。内存屏障强制读或写操作顺序限制不是单 向的。强制读和写操作顺序限制的内存屏障是双向的, 类似于双向开的栅栏。使 用ld.acq就是单向内存屏障的例子。 一致性具有两面性。如果一个读线程在两次读操作之间插入了内存屏障而另外 一个线程没有在两次写操作之间添加内存屏障又有什么用呢?线程为了协调,必 须同时遵守这个协议,就像网络中的节点或者团队中的成员。如果某个线程破坏 了这个约定,那么其他所有线程的努力都白费。Dekker算法的最后两行代码的汇 编指令应该插入一个内存屏障,两次volatile写之间。 $ java -XX:+UnlockDiagnosticVMOptions - XX:PrintAssemblyOptions=hsdis-print-bytes - XX:CompileCommand=print,WriterReader.write WriterReader
这里我们可以看到在第四行第二次写操作被注解了一个显式内存屏障。通过使 用st.rel,即“存储释放”(store release),编译器确保第一次写操作在第二 次写操作之前完成。这就完成了两边的 |
凌众科技专业提供服务器租用、服务器托管、企业邮局、虚拟主机等服务,公司网站:http://www.lingzhong.cn 为了给广大客户了解更多的技术信息,本技术文章收集来源于网络,凌众科技尊重文章作者的版权,如果有涉及你的版权有必要删除你的文章,请和我们联系。以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢! |