What is volatile?

时间:2021-05-04 13:42:59

What is volatile?

一次偶然的机会(java多线程电梯作业寻求多个进程分享变量的方法),接触到了volatile,因此我查阅了相关的材料,对这部分做了一些了解,在这里和大家分享一下。

首先,我们先来聊一聊几个概念

1、What is reorder

编译器和JVM通过改变程序的处理顺序来优化程序,用于提高程序性能的方式。

多线程程序设计中,重排序会导致运行错误。

举个栗子:

class Compare {
   private int x = 0;
   private int y = 0;
   
   public void write() {
       x = 100;
       y = 50;
  }
   
   public boolean compare() {
       return x<y
  }
}

public class Main {
   public static void main(String[] args) {
       final Compare com = new Compare();
       new Thread() {
           public void run() {
               com.write();
          }
      }.start();
       new Thread() {
           public void run() {
               com.read();
          }
      }.start();
  }
}

让人吃惊的是,x<y居然真的存在true的情况

原因就在于重排序

编译器的优化策略可能会改变x,y的赋值顺序,导致x<y

显然,我们可以通过synchronized来解决这个问题

2、What is visibility

线程A将某个值写入字段x,线程B读到了这个值

多线程中的可见性问题来源于normal read/write操作是通过缓存在执行的,read到的不一定是最新值,write的也不一定立即对其他线程可见

而synchronized是解决这一问题的有效方法,相信大家也并不陌生,具体用法可以参考我的另一篇博客

其实,volatile也是一种不错的方法

3、What is atomicity

不可分割的操作,例如某线程正在执行synchronized方法,其他线程无法进入该方法,从多线程的角度,这就是原子操作。

Java定义了一些原子操作:primitive type(char、int)的赋值和引用,对象等引用类型的赋值和引用

但是long与double的操作不是原子的,在线程共享时需要放入synchronized

now,进入正题

Volatile

Make sure that a given variable is read directly from main memory and always written back to main memory when updated

volatile具有同步处理(参考sunchronization)和对long和double的原子操作这两种功能

同步处理

1、如果线程A向volatile字段写入的值对线程B可见,那么之前向其他字段写入的值都是对B可见

2、向volatile字段读取和写入前后不会发生重排序

看到这里,我们发现重排序和可见性的问题好像都被volatile解决了

在这里,我们来看一段代码,深入理解一下同步处理

class TryVolatile {
   private int num = 0;
   private volatile boolean valid = false;
   
   public void write() {
       num = 1;
       valid = true;
       /*
        *1、不会被重排序
        *2、线程B中valid会为true
        *3、线程B可能出现num=1
        */
  }
   
   public void read() {
       if (valid) {
           System.out.println(this.num);
      }
  }
}

public class Main {
   public static void main(String[] args) {
       final tryVolatile = new TryVolatile;
       new Thread() {
           public void run() {
               tryVolatile.write();
          }
      }.start();
       new Thread() {
           public void run() {
               tryVolatie.read();
          }
      }.start();
  }
}

总结一下volatile的使用:

1、volatile字段赋值语句位置很重要!!!(可以运行上面的代码观察

2、volatile不会进行线程互斥处理(volatile字段不会进入等待队列

3、访问volatile字段会产生性能开销(参考synchronized

对long和double的原子操作

tip:

java.util.concurrent.atomic包提供了原子操作编程的类,例如AtomicInteger、AtomicLong、AtomicIntegerArray等,都是通过封装volatile得到的