Volatile详解

简介

volatile是一个特征修饰符(type specifier).volatile的作用是作为指令关键字,确保本条指令不会因编译器的优化而省略,且要求每次直接读值。volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。

共享变量的可见性

Volatile详解
共享变量存储在主内存(Main Memory)中,每个线程都有一个私有的本地内存(Local Memory),本地内存保存了被该线程使用到的主内存的副本拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存中的变量。对于普通的共享变量来讲,线程A将其修改为某个值发生在线程A的本地内存中,此时还未同步到主内存中去;而线程B已经缓存了该变量的旧值,所以就导致了共享变量值的不一致。

Volatile的特性和原理以及使用场景

一.特性
1)保证可见性,不保证原子性

a.当写一个volatile变量时,会把该线程本地内存中的变量强制刷新到主内存中去;

b.这个写操作会导致其他线程中的缓存无效。

(2)禁止指令重排

重排序是指编译器和处理器为了优化程序性能而对指令序列进行排序的一种手段。

重排序操作不会对存在数据依赖关系的操作进行重排序。

比如:a=1;b=a; 这个指令序列,由于第二个操作依赖于第一个操作,所以在编译时和处理器运

行时这两个操作不会被重排序

二.原理
volatile可以保证线程可见性且提供了一定的有序性,但是无法保证原子性。在JVM底层volatile是采用“内存屏障”来实现的。观察加入volatile关键字和没有加入volatile关键字时所生成的汇编代码发现,加入volatile关键字时,会多出一个lock前缀指令,lock前缀指令实际上相当于一个内存屏障,内存屏障会提供3个功能:

I. 它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内

存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成;

II. 它会强制将对缓存的修改操作立即写入主存;

III. 如果是写操作,它会导致其他CPU中对应的缓存行无效。
三.使用场景
Volatile详解
需要volatile关键字的原因是,在并发情况下,如果没有volatile关键字,在第5行会出现问题。instance = new TestInstance();可以分解为3行伪代码

a.memory = allocate() //分配内存

b. ctorInstanc(memory) //初始化对象

c. instance = memory //设置instance指向刚分配的地址

上面的代码在编译运行时,可能会出现重排序从a-b-c排序为a-c-b。在多线程的情况下会出现以下问题。当线程A在执行第5行代码时,B线程进来执行到第2行代码。假设此时A执行的过程中发生了指令重排序,即先执行了a和c,没有执行b。那么由于A线程执行了c导致instance指向了一段地址,所以B线程判断instance不为null,会直接跳到第6行并返回一个未初始化的对象。