volatile关键字简介
开始
volatile关键字,模板化概念我就不写了,网上大把。
我们还是保持本博客的传统,从最笨的理解开始说起:
- 先大概了解几个概念:线程、内存(了解计算机内存就行,本文不涉及JMM)
- 线程是干活的,干活就需要用到数据,数据存在哪里呢?需要长期保存的就做持久化(存入数据库、文件等),干活期间临时使用的数据(也就是变量),这个存在内存中。
- 线程是在cpu中的(进程中),线程中使用变量的时候,是从内存中读取/写入。硬件层面也就是CPU访问内存条。
- 虽然内存条的性能也很好,但CPU性能远远高于内存条,为了使CPU读写数据的时候尽量不被内存条性能影响,在CPU中增加了一个缓存模块,即CPU缓存。
- 此后,CPU的读写操作都是对CPU缓存的读写,CPU缓存中的数据会选择合适的时机去和主内存同步。如图:

- 这样读写性能是高了,但是带来了新的问题:
因为有了CPU缓存,每个线程有自己的工作内存,当对一个变量写入值之后,其他线程不能及时获取到新值(因为还没同步,同步后就是新值了)。这就是内存可见性问题。 - volatile关键字就是为了解决内存可见性问题的,被volatile修饰的变量,程序对其进行读写操作时,都是直接操作主内存的,就解决了内存可见性问题。
- 有人说了,那被volatile修饰的变量不就是没用到cpu缓存吗?那性能不是下降了吗?
- 没错,你说的是对的。性能确实下降了。
扩展
- 在其他文章中看到volatile是解决了两个问题:
- 内存可见性
- 禁止指令重排序
- 上面只介绍了内存可见性问题的解决,没有说禁止指令重排序啊
- 没错,下面说这个指令重排序,因为这个“禁止指令重排序”也是为了解决内存可见性问题的。
- 先说一下啥是指令重排序,指令指的是cpu指令。
- 比如,我们要定义一个变量并赋值,java代码是这样写:
编译成cpu指令是这样的(逻辑代码),cpu指令重排序之前:int num = 1;
cpu指令重排序之后:定义一个引用 num 申请一块内存空间,拿到空间地址 将值1写入这块内存空间 将这块空间地址赋值给num定义一个引用 num 申请一块内存空间,拿到空间地址,赋值给num 将值1写入这块内存空间 - 大家发现问题了吗,cpu是先把内存空间的引用赋值给num,再写入真实的值1的。cpu这么做的目的是为了更快的响应(个人初步猜测,需要深入研究寻找证据或推翻猜测)。如果第二句指令执行完了,第三句还没执行的时候,我们去读取num的值,读到的是空的。
- volatile的禁止重排序就是为了解决上面的问题。

浙公网安备 33010602011771号