volatile做用总结:html
1. 强制线程从公共内存中取得变量的值,而不是从线程的私有的本地内存中,volatile修饰的变量不具备原子性(修改一个变量的值不能同步)。缓存
2. 保证volatile修饰的变量在被一个线程修改后,会被强制当即刷新到主存(可见性),其余线程若是有该变量的缓存行,会被设置为无效。app
3. 禁止指令重排(有序性)。性能
a.happen-before:http://www.javashuo.com/article/p-ttuhluqc-cv.html; 优化
b.编译器在生成字节码时, 会在指令序列中插入内存屏障,会多出一个 lock 前缀指令spa
内存屏障是一组处理器指令,解决禁止指令重排序和内存可见性的问题,保证指令重排序后与以前的输出结果一 样,使性能获得优化。处理器在进行重排序时是会考虑指令之间 的数据依赖性。线程
指令重排:Java 语言规范规定了JVM线程内部维持顺序化语义,也就是说只要程序的最终结果等同于它在严格的顺序化环境下的结果,那么指令的执行顺序就可能与代码的顺序不一致。这个过程经过叫作指令的重排序。htm
指令重排序存在的意义在于:JVM可以根据处理器的特性(CPU的多级缓存系统、多核处理器等)适当的重新排序机器指令,使机器指令更符合CPU的执行特色,最大限度的发挥机器的性能,提升效率。blog
双重校验锁 DCL(double checked locking)--使用 volatile 的场景之一。排序
一旦一个共享变量(类的成员变量、类的静态成员变量)被 volatile 修饰 以后,那么就具有了两层语义:
1)保证了不一样线程对这个变量进行读取时的可见性,即一个线程修改 了某个变量的值,这新值对其余线程来讲是当即可见的。(volatile 解决了 线程间共享变量的可见性问题)。
第一:使用 volatile 关键字会强制将修改的值当即写入主存;
第二:使用 volatile 关键字的话,当线程 2 进行修改时,会致使线程 1 的 工做内存中缓存变量 stop 的缓存行无效(反映到硬件层的话,就是 CPU 的 L1 或者 L2 缓存中对应的缓存行无效);
第三:因为线程 1 的工做内存中缓存变量 stop 的缓存行无效,因此线程 1 再次读取变量 stop 的值时会去主存读取。 那么,在线程 2 修改 stop 值时(固然这里包括 2 个操做,修改线程 2 工 做内存中的值,而后将修改后的值写入内存),会使得线程 1 的工做内存中缓 存变量 stop 的缓存行无效,而后线程 1 读取时,发现本身的缓存行无效,它会 等待缓存行对应的主存地址被更新以后,而后去对应的主存读取最新的值。 那么线程 1 读取到的就是最新的正确的值。
2)禁止进行指令重排序,阻止编译器对代码的优化。
volatile 关键字禁止指令重排序有两层意思: I)当程序执行到 volatile 变量的读操做或者写操做时,在其前面的操做的 更改确定所有已经进行,且结果已经对后面的操做可见;在其后面的操做确定 尚未进行; II)在进行指令优化时,不能把 volatile 变量前面的语句放在其后面执行, 也不能把 volatile 变量后面的语句放到其前面执行。 为了实现 volatile 的内存语义,加入 volatile 关键字时,编译器在生成字节码时, 会在指令序列中插入内存屏障,会多出一个 lock 前缀指令。
内存屏障,有 2 个做用:1.先于这个内存屏障的指令必须先执行,后于这个内存屏障 的指令必须后执行。2.使得内存可见性。因此,若是你的字段是 volatile,在读指令前插入读屏障,可让高速缓存中的数据失效,从新从主内存加载数据。在写指令以后插入写屏障,能让写入缓存的最新数据写回到主内存。
lock 前缀指令在多核处理器下会引起了两件事情: 1.将当前处理器中这个变量所在缓存行的数据会写回到系统内存。这个写回内存的 操做会引发在其余 CPU 里缓存了该内存地址的数据无效。可是就算写回到内存,若是 其余处理器缓存的值仍是旧的,再执行计算操做就会有问题,因此在多处理器下,为了 保证各个处理器的缓存是一致的,就会实现缓存一致性协议,每一个处理器经过嗅探在总 线上传播的数据来检查本身缓存的值是否是过时了,当处理器发现本身缓存行对应的内 存地址被修改,就会将当前处理器的缓存行设置成无效状态,当处理器要对这个数据进 行修改操做的时候,会强制从新从系统内存里把数据读处处理器缓存里。 2.它确保指令重排序时不会把其后面的指令排到内存屏障以前的位置,也不会把前面 的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操做已经全 部完成。
内存屏障能够被分为如下几种类型:
LoadLoad 屏障:对于这样的语句 Load1; LoadLoad; Load2,在 Load2 及后续读取 操做要读取的数据被访问前,保证 Load1 要读取的数据被读取完毕;
StoreStore 屏障:对于这样的语句 Store1; StoreStore; Store2,在 Store2 及后续写 入操做执行前,保证 Store1 的写入操做对其它处理器可见;
LoadStore 屏障:对于这样的语句 Load1; LoadStore; Store2,在 Store2 及后续写 入操做被刷出前,保证 Load1 要读取的数据被读取完毕;
StoreLoad 屏障:对于这样的语句 Store1; StoreLoad; Load2,在 Load2 及后续所 有读取操做执行前,保证 Store1 的写入对全部处理器可见;它的开销是四种屏障中最 大的。在大多数处理器的实现中,这个屏障是个万能屏障,兼具其它三种内存屏障的功 能。