浅谈volatile

简介: 浅谈volatile

一旦一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:

保证线程间的可见性

保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的,volatile关键字会强制将修改的值立即写入主存。

一个典型的例子:永不停止的循环

packagecom.itheima.basic;



// 可见性例子

// -Xint

publicclassForeverLoop {

  staticbooleanstop=false;


  publicstaticvoidmain(String[] args) {

      newThread(() -> {

          try {

              Thread.sleep(100);

         } catch (InterruptedExceptione) {

              e.printStackTrace();

         }

          stop=true;

          System.out.println("modify stop to true...");

     }).start();

      foo();

 }


  staticvoidfoo() {

      inti=0;

      while (!stop) {

          i++;

     }

      System.out.println("stopped... c:"+i);

 }

}

当执行上述代码的时候,发现foo()方法中的循环是结束不了的,也就说读取不到共享变量的值结束循环。

主要是因为在JVM虚拟机中有一个JIT(即时编辑器)给代码做了优化。

上述代码

while (!stop) {

i++;

}

在很短的时间内,这个代码执行的次数太多了,当达到了一个阈值,JIT就会优化此代码,如下:

while (true) {

i++;

}

当把代码优化成这样子以后,及时stop变量改变为了false也依然停止不了循环

解决方案:

第一:

在程序运行的时候加入vm参数-Xint表示禁用即时编辑器,不推荐,得不偿失(其他程序还要使用)

第二:

在修饰stop变量的时候加上volatile,表示当前代码禁用了即时编辑器,问题就可以解决,代码如下:

staticvolatilebooleanstop=false;

禁止进行指令重排序

volatile 修饰共享变量会在读、写共享变量时加入不同的屏障,阻止其他读写操作越过屏障,从而达到阻止重排序的效果

在去获取上面的结果的时候,有可能会出现4种情况

情况一:先执行actor2获取结果--->0,0(正常)

情况二:先执行actor1中的第一行代码,然后执行actor2获取结果--->0,1(正常)

情况三:先执行actor1中所有代码,然后执行actor2获取结果--->1,1(正常)

情况四:先执行actor1中第二行代码,然后执行actor2获取结果--->1,0(发生了指令重排序,影响结果)

解决方案

在变量上添加volatile,禁止指令重排序,则可以解决问题

屏障添加的示意图

·        写操作加的屏障是阻止上方其它写操作越过屏障排到volatile变量写之下

·        读操作加的屏障是阻止下方其它读操作越过屏障排到volatile变量读之上

其他补充

我们上面的解决方案是把volatile加在了int y这个变量上,我们能不能把它加在int x这个变量上呢?

下面代码使用volatile修饰了x变量

屏障添加的示意图

这样显然是不行的,主要是因为下面两个原则:

·        写操作加的屏障是阻止上方其它写操作越过屏障排到volatile变量写之下

·        读操作加的屏障是阻止下方其它读操作越过屏障排到volatile变量读之上

所以,现在我们就可以总结一个volatile使用的小妙招:

·        写变量让volatile修饰的变量的在代码最后位置

·        读变量让volatile修饰的变量的在代码最开始位置

相关文章
|
4月前
|
缓存 Java 编译器
volatile与synchronized
volatile与synchronized
36 0
|
10月前
|
缓存 Java 编译器
|
存储 SQL 缓存
|
SQL
volatile的正确使用(七)
volatile的正确使用(七)
86 0
volatile的正确使用(七)
volatile与JMM(二)
问:volatile凭什么可以保证可见性和有序性 答: 内存屏障
73 0
volatile与JMM(二)
|
缓存
volatile
volatile
77 0
|
SQL 存储 算法
volatile详解
在单线程环境中,我们几乎用不到这个关键词,但是多线程环境中,这个关键词随处可见。而且也是面试的常客。总的来说,volatile有以下三个特性: 保证可见性; 不保证原子性; 禁止指令重排。 下面就来详细的说说这三个特性。
volatile详解
|
存储 缓存 安全
synchronized&volatile (二)
synchronized&volatile (二)
165 0
|
缓存 安全 Java
synchronized&volatile (一)
synchronized&volatile (一)
109 0