volatile关键字与Java内存模型(JMM)

Java内存模型(JMM)

JMM用来屏蔽不一样硬件和操做系统的内存访问差别,指望Java程序在各类平台上都能实现一致的内存访问效果;


 
JMM规定里多线程之间的共享变量存储在主存中, 每一个线程单独拥有一个本地内存( 逻辑概念 ),本地内存存储线程操做的共享变量副本;
  • JMM中的变量指的是线程共享变量(实例变量,static字段和数组元素),不包括线程私有变量(局部变量和方法参数);
  • JMM规定线程对变量的写操做都在本身的本地内存对副本进行,不能直接写主存中的对应变量;
  • 多线程间变量传递经过主存完成(Java线程通讯经过共享内存),线程修改变量后经过本地内存写回主存,从主存读取变量,彼此不容许直接通讯(本地内存私有缘由);
综上,JMM经过控制主存和每一个线程的本地内存的数据交互,保证 一致的内存 可见性;

JMM特征

原子性:原子性是指多线程一块儿执行时,一个线程操做开始后不会被其余线程干扰,操做不可被中断;
  • synchronizd临界区执行具备原子性;
  • volatile仅仅保证对单个volatile变量的操做具备原子性;
可见性:一个线程修改共享变量时,其余线程可以当即知道这个修改;
  • 单线程:不存在内存可见性问题;
  • 多线程:Java经过volatile, synchronized, final关键字实现可见性;
    • volatilevalatile变量保证变量新值当即被同步回主存,每次读取valtile变量都当即从主存刷新;
    • synchronized:对变量进行解锁前,将对应变量同步回内存;
    • final:final字段一旦初始化完毕,而且this引用没有发生逃逸,其余线程当即看到final字段值;
有序性:线程内操做有序进行,线程间操做有序进行;
  • Java经过volatilesynchronized保证线程间操做的有序性
    • volatile经过禁止重排序实现有序性;
    • synchronized经过声明临界区,保证线程互斥访问,实现有序性;

synchronized与volatile辨析

  • volatile是线程同步的轻量级实现,只用于修饰变量,synchronized用于修饰方法和语句块;
  • 多线程访问volatile不会发生阻塞,可是synchronized会发生阻塞;
  • volatile保证数据的可见性,不保证原子性;synchronized保证数据的可见性和原子性;
  • volatile强调共享变量在多线程之间的可见性,synchronized强调多线程访问资源的同步性;


重排序与happen-before规则

影响多线程有序性:重排序

  • 编译器重排序:编译器保证不改变单线程执行结果的前提下,能够调整多线程语句执行顺序;
  • 处理器重排序若是不存在数据依赖性,处理器能够改变语句对应机器指令的执行顺序;
JMM经过happen-before规则,底层禁止特定类型的编译器重排序和处理器重排序,保证内存的可见性和有序性

happen-before规则

JMM为全部操做定义了一个偏序关系,称之为happen-before。 在JMM中,若是A操做对B操做存在happen-before关系;A操做的执行结果所有对B操做可见; 所以不一样操做的时间顺序和先行发生规则没有关系,happen-before强调前者修改结果所有对后者可见若是A,B操做不存在happen-before关系,JVM会对它们进行任意重排序;
JMM默认 happen-before 规则:
  • 程序顺序规则:一个线程的每一个操做,先于该线程其余后续操做执行;
    • 线程的start()方法先于线程内其余方法执行,线程全部操做先于线程的终结操做;
  • 锁规则:对一个monitor的解锁必然先于对该monitor的加锁;
  • volatile变量规则:对volatile的写操做先于读操做;
  • 传递性:A先于B,B先于C,必然A先于C;

内存屏障指令(memory barriers)

内存屏障指令是一组处理指令,用来限制内存操做的顺序;

volatile关键字

★ volatile实现原理 

volatile变量写,汇编指令会多出 Lock前缀,Lock前缀在多核处理器下的做用:
  • 将当前处理器缓存行的数据写回主存;
  • 令其余CPU里缓存该内存地址的数据无效;
针对编译器重排序: JMM针对编译器指定了volatile重排序规则表,规定哪些前后操做不能进行编译器重排序:
针对处理器重排序:编译器在生成字节码指令时,经过在指令序列中插入内存屏障指令来禁止特定类型的处理器重排序,以 实现volatile内存语义 volatile底层经过内存屏障指令实现
  • 在每一个volatile变量写操做以前插入StoreStore屏障,以后插入StoreLoad屏障;
    • 以前插入StoreStore屏障:禁止volatile写以前的写操做与其重排序,保证以前的全部写操做都写回主存,对volatile写可见
    • 以后插入StoreLoad屏障:禁止volatile写以后的读写操做与其重排序,实现volatile写结果对后续操做可见
  • 在每一个volatile变量读操做以后,接连插入LoadLoad屏障,LoadStore屏障;
    • 插入LoadLoad屏障:禁止volatile变量读以后的读操做与其重排序;
    • 插入LoadStore屏障:禁止volatile变量读以后的写操做与其重排序;
    • 经过插入两次内存屏障,实现volatile读结果对后续操做可见
JMM经过上述内存屏障插入策略,保证在任意平台上 volatile 的内存语义一致;

volatile关键字语义

volatile用来修饰共享变量(成员变量,static变量)代表:
  • volatile变量:当写一个volatile变量时,JMM会把全部线程本地内存的对应变量副本刷新回主存;
    • volatile写和解锁内存语义相同;
  • volatile变量:当读一个volatile变量时,JMM会设置该线程的volatile变量副本(本地内存中)无效,线程只能从主存中读取该变量;
    • 保证了volatile变量读,总能看见对该volatile变量最后的修改;
    • volatile变量读和加锁内存语义相同;
经过上述机制, volatile 保证共享变量一旦被修改,新值对全部线程可见;
相关文章
相关标签/搜索