扫码关注微信公众号

回复“面试手册”,获取本站PDF版

回复“简历”,获取高质量简历模板

回复“加群”,加入程序员交流群

回复“电子书”,获取程序员类电子书

当前位置: Java > Java并发高频面试题 > 47.volatile如何实现有序性

前面提到重排序可以提高代码的执行效率,但在多线程程序中可以导致程序的运行结果不正确,那volatile是如何解决这一问题的呢?

为了实现volatile的内存语义,编译器在生成字节码时会通过插入内存屏障来禁止指令重排序。

内存屏障:内存屏障是一种CPU指令,它的作用是对该指令前和指令后的一些操作产生一定的约束,保证一些操作按顺序执行。

Java虚拟机插入内存屏障的策略

Java内存模型把内存屏障分为4类,如下表所示:

屏障类型指令示例说明
LoadLoad BarriersLoad1;LoadLoad;Load2保证Load1数据的读取先于Load2及后续所有读取指令的执行
StoreStore BarriersStore1;StoreStore;Store2保证Store1数据刷新到主内存先于Store2及后续所有存储指令
LoadStore BarriersLoad1;LoadStore;Store2保证Load1数据的读取先于Store2及后续的所有存储指令刷新到主内存
StoreLoad BarriersStore1;StoreLoad;Load2保证Store1数据刷新到主内存先于Load2及后续所有读取指令的执行

注:StoreLoad Barriers同时具备其他三个屏障的作用,它会使得该屏障之前的所有内存访问指令完成之后,才会执行该屏障之后的内存访问命令。

Java内存模型对编译器指定的volatile重排序规则为:

  • 当第一个操作是volatile读时,无论第二个操作是什么都不能进行重排序。
  • 当第二个操作是volatile写时,无论第一个操作是什么都不能进行重排序。
  • 当第一个操作是volatile写,第二个操作为volatile读时,不能进行重排序。

根据volatile重排序规则,Java内存模型采取的是保守的屏障插入策略,volatile写是在前面和后面分别插入内存屏障,volatile读是在后面插入两个内存屏障,具体如下:

  • volatile读:在每个volatile读后面分别插入LoadLoad屏障及LoadStore屏障(根据volatile重排序规则第一条),如下图所示

volatile读
volatile读

LoadLoad屏障的作用:禁止上面的所有普通读操作和上面的volatile读操作进行重排序。

LoadStore屏障的作用:禁止下面的普通写和上面的volatile读进行重排序。

  • volatile写:在每个volatile写前面插入一个StoreStore屏障(为满足volatile重排序规则第二条),在每个volatile写后面插入一个StoreLoad屏障(为满足volatile重排序规则第三条),如下图所示

volatile写
volatile写

StoreStore屏障的作用:禁止上面的普通写和下面的volatile写重排序

StoreLoad屏障的作用:防止上面的volatile写与下面可能出现的volatile读/写重排序。


点击面试手册,获取本站面试手册PDF完整版