volatile 用于提供顺序和可见性,volatile 类型变量即便在没有同步块的状况下赋值也不会与其余语句重排序,volatile 所修饰的变量的修改会马上写到主存去,解决了可见性的问题,concurrent 包中大量使用了 volatile 来修饰状态。
操做系统内存
计算机在运行程序时,每条指令都是在 CPU 中执行的,在执行过程当中势必会涉及到数据的读写。咱们知道程序运行的数据是存储在主存中,这时就会有一个问题,读写主存中的数据没有 CPU 中执行指令的速度快,若是任何的交互都须要与主存打交道则会大大影响效率,因此就有了 CPU 高速缓存。CPU 高速缓存为某个 CPU 独有,只与在该 CPU 运行的线程有关。
解决缓存一致性方案有两种:缓存
Java内存模型
Java 内存模型规定全部的对象都是存在主存当中,每一个线程都有本身的工做内存。线程对变量的全部操做都必须在工做内存中进行,而不能直接对主存进行操做。而且每一个线程不能访问其余线程的工做内存。线程执行的时候用到某变量,首先要将变量从主内存拷贝的本身的工做内存空间,而后对变量进行操做:读取,修改,赋值等,这些均在工做内存完成,操做完成后再将变量写回主内存。
Java 提供了 volatile 关键字来保证可见性。当一个共享变量被 volatile 修饰时,它会保证修改的值会当即被更新到主存,当有其余线程须要读取时,它会去内存中读取新值。而普通的共享变量不能保证可见性,由于普通共享变量被修改以后,何时被写入主存是不肯定的,当其余线程去读取时,此时内存中可能仍是原来的旧值,所以没法保证可见性。
volatile实现原理
volatile 在 JVM 底层是经过内存屏障(memory barrier)实现的。当你写一个 volatile 变量以前,Java 内存模型会插入一个写屏障(write barrier);读一个 volatile 变量以前,会插入一个读屏障(read barrier)。在你写一个 volatile 域时,能保证任何线程都能看到你写的值;在写以前,也能保证任何数值的更新对全部线程是可见的,由于内存屏障会将其余全部写的值更新到缓存。
内存屏障(meomory barrier)
序在运行时内存实际的访问顺序和程序代码编写的访问顺序不必定一致,这就是内存乱序访问。内存乱序访问行为出现的理由是为了提高程序运行时的性能。内存乱序访问主要发生在两个阶段:多线程
内存屏障有两个能力:jvm
volatile 保证 long 和 double 的原子性
long 和 double 两种数据类型的操做可分为高32位和低32位两部分,对 long 和 double 的操做也是分两次完成。在64位机上使用64位的 jvm 中 long 和 double 的操做是原子的。因此,官方鼓励使用 volatile 修饰 long 和 double。
使用场景性能