天天看点

浅析JAVA内存模型之volatile

我们抛开复杂的Java内存模型(JMM)规范, 用最简单的方法看下一个普通变量为什么是线程不安全的; volatile又是怎么做到数据的轻量级同步的.

首先, 线程在运行时, 并不是直接从主内存中去读写变量的, 因为这种读写速度是跟不上CPU计算速度的.

CPU不会直接读/写主内存中变量数据, 而是将寄存器中的数据先写入L1/L2 cache, 在写入主内存中, 读的过程也是先读入L1/L2 cache中, 寄存器从L1/L2 cache中读取计算.

浅析JAVA内存模型之volatile

类似的, java线程内存模型也有自己的工作内存, CPU先将数据读取到工作内存中, 在进行计算;

需要注意的是, 这时工作内存中的数据, 都是原数据的变量副本; 所以多线程的情况下, 各线程间是无法访问和感知变量修改的, 这样也就造成了线程不安全问题.

浅析JAVA内存模型之volatile

那volatile是如何做到线程安全的呢?

在说明volatile之前, 我们先完善一个知识点, 这些工作内存中的变量并不是直接与主内存交互的, 而是通过总线进行读写操作的.

浅析JAVA内存模型之volatile

使用volatile修饰的变量在编译器编译后, 会多出一个lock前缀的汇编指令, 相当于一个内存屏障, 会将修改的数据强制写入主内存, 而不是缓存在工作内存中.

在变量更新时, 根据总线MESI缓存一致性协议, 其他CPU通过总线嗅探机制, 感知变量发生变化, 并将自己工作内存中的变量副本无效化.

可见volatile变量是保证了线程间的可见性; 但并不保证原子性, 在多线程同时读写的时候是需要借助CAS或者锁相关机制的.

根据java内存模型中的happens-before原则, volatile变量还会保证有序性, 也就是对一个变量的写操作先行发生于后面对这个变量的读操作;