大纲
一、为什么有Volatile?
-
类似:了解过JMM内存模型吗?
多线程环境下,一个共享变量可能存在于多个位置,如主内存、多个本地内存,可能带来数据不一致的问题。不一致有两方面原因,可见性和有序性。 -
可见性
一个线程对主内存数据进行了修改,而另外一个线程不知道,导致共享变量值不一样。 -
有序性
多线程交替执行,由于编译器优化重排的存在,变量无法保证一致性。
……处理器指令间的数据依赖性……
二、Volatile是什么?
1、volatile的作用是什么?
类似:什么是内存可见性?能否举例说明?什么又是重排序呢?能举例说明吗?
- 可见性
当一个线程改完一个共享变量会立刻刷新到主内存,并强制清空该变量的缓存数据,必须从主内存重新读取最新数据。 - 有序性
禁止指令重排序,在一定程度上保证有序性。
volatile关键字禁止指令重排序有两层意思:
1)当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行;
2)在进行指令优化时,不能将在对volatile变量的读操作或者写操作的语句放在其后面执行,也不能把volatile变量后面的语句放到其前面执行。
追问1:volatile能保证原子性吗?为什么?
- 类似:i++为什么不能保证原子性?
不能完全保证,只能保证单次的读/写操作具有原子性。比如,i++,这是是一个复合操作:
- 读取 i 值;
- 对 i 加 1;
- 将 i 值写回内存。
volatile无法保证这三个操作有原子性。
// 通过AtomicInteger或Synchronized来保证+1操作的原子性。
public class P0Test {
private volatile int value; //将value变量声明成volatile类型
public void increment(){ //synchronized
i++;
System.out.println(i);
}
public static void main(String[] args) {
final P0Test volatileTest1 = new P0Test();
for(int i = 0; i < 100; i ++){
new Thread(new Runnable() {
@Override
public void run() {
volatileTest1.increment();
}
}).start();
}
}
}
2、说下volatile的应用场景?
一次读一次写。
- 运算结果不依赖变量的当前值。
--》不能是自增量(如x++)
- 变量不需要与其他的转态变量共同参与不变的约束。
--》不变的约束即为条件表达式(start < end),若表达式使用了两个及以上变量(不管是否是volatile变量)就无法保证正确性,即不变式会失效。
如:一写多读。
追问1:Volatile是否用得越多越好?
不是,可能导致总线风暴。 由于Volatile的MESI缓存一致性协议,需、要不断从主内存嗅探和CAS循环,无效交互会导致总线带宽达到峰值,所以不要大量使用Volatile。
3、之前32位机器上共享的long和double变量的为什么要用volatile? 现在64位机器上是否也要设置呢?
因为long和double操作可分为高32位和低32位两部分,因此普通long或double类型读/写可能不是原子。因此,家将共享long和double变量设置为volatile类型能保证任何情况下单次读/写操作的原子性。
4、happens-before等?
……
三、Volatile怎么实现的?
类似:底层原理是怎么实现的吗?怎么保证有序性的?volatile如何实现可见性?volatile如何实现有序性?
1、可见性
volatile变量修饰会出现Lock前缀指令,在多核处理器中会引发两件事:
- 将当前处理器缓存行数据写回到系统内存
- 然后使其他CPU里缓存了该内存地址的数据无效
2、有序性
Lock前缀指令相当于一个内存屏障/栅栏,让volatile上面、volatile修饰变量、volatile下面的执行顺序保持相对不变。
四、Volatile延申?
1、synchronized和Volatile区别是什么?
- volatile本质是在告诉JVM当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取;synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
- volatile仅能使用在变量级别;synchronized则可以使用在变量、方法和类级别的;
- volatile仅能实现变量的修改可见性,不能保证原子性;而synchronized则可以保证变量的修改可见性和原子性;
- volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
- volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化。
2、……
JVM的底层操作、字节码的操作、单例
为什么 AtomicInteger 可以保证原子性,怎么实现?
……
五、参考
-
自己整理
1、【面试】Volatile详解 -
面试
1、面试官没想到一个Volatile,我都能跟他扯半小时
2、关键字: volatile详解
3、Java面试官最爱问的volatile关键字 -
主要内容
1、Java多线程volatile详解
2、Java 之 volatile 详解 -
代码示例
1、详解java中的并发关键字volatile