volatile关键字的特点和底层实现
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了volatile关键字的特点和底层实现相关的知识,希望对你有一定的参考价值。
1.一个 volatile 变量自身具有以下三个特性:
(1)可见性:即当一个线程修改了声明为 volatile 变量的值,新值对于其他要读该变量的线程来说是立即可见的。而普通变量是不能做到这一点的,普通变量的值在线程间传递需要通过主内存来完成。
底层实现:volatile 可见性的实现就是借助了 CPU 的 lock 指令,通过在写 volatile 的机器指令前加上
lock 前缀,使写 volatile 具有以下两个原则:
a.写 volatile 时处理器会将缓存写回到主内存。
b.一个处理器的缓存写回到内存会导致其他处理器的缓存失效
(2)有序性:volatile 变量的所谓有序性也就是被声明为 volatile 的变量的临界区代码的执行是有顺序的,即禁止指令重排序。
底层实现:volatile 有序性的保证就是通过禁止指令重排序来实现的。指令重排序包括编译器和处理器重排序,JMM 会分别限制这两种指令重排序。
那么禁止指令重排序又是如何实现的呢?答案是加内存屏障。JMM 为 volatile 加内存屏障有
以下 4 种情况:
a.在每个 volatile 写操作的前面插入一个 StoreStore 屏障,防止写 volatile 与后面的写操作重排序。
b.在每个 volatile 写操作的后面插入一个 StoreLoad 屏障,防止写 volatile 与后面的读操作重排序。
c.在每个 volatile 读操作的后面插入一个 LoadLoad 屏障,防止读 volatile 与后面的读操作重排序。
d.在每个 volatile 读操作的后面插入一个 LoadStore 屏障,防止读 volatile 与后面的写操作重排序。
上述内存屏障的插入策略是非常保守的,比如一个 volatile 的写操作后面需要加上storeStore 和 StoreLoad 屏障,但这个写 volatile 后面可能并没有读操作,因此理论上只加上StoreStore 屏障就可以,的确,有的处理器就是这么做的。但 JMM 这种保守的内存屏障插入策
略能够保证在任意的处理器平台,volatile 变量都是有序的。
(3)受限原子性:这里 volatile 变量的原子性与 synchronized 的原子性是不同的,synchronized的原子性是指只要声明为 synchronized 的方法或代码块儿在执行上就是原子操作的。而 volatile是不修饰方法或代码块儿的,它用来修饰变量,对于单个volatile变量的读/写操作都具有原子性,但类似于 volatile++这种复合操作不具有原子性。所以 volatile 的原子性是受限制的。并且在多
线程环境中,volatile 并不能保证原子性
以上是关于volatile关键字的特点和底层实现的主要内容,如果未能解决你的问题,请参考以下文章
Juc12_Volatile的可见性不保证可见性有序性使用内存屏障四大指令StoreStoreStoreLoad LoadLoadLoadStore