volatile实现原理

java内存模型(Java Memory Model,JMM)

java内存模型

这张图相信已经看过很多遍了,《深入理解java虚拟机》中解释如下,我完整写过来:
“由于计算机的储存设备与处理器的运算速度有着几个数量级的差距,所以现代计算机系统都不得不加入一层或多层读写速度尽可能接近处理器运算速度的告诉缓存(Cache)来作为内存和处理器之间的缓冲:将运算需要使用的数据复制到缓存中,让运算能快速进行,当运算结束再从缓存同步回内存之中,这样处理器就无序等待缓慢的内存读写。”
很明显加入了高速缓存后,会引入新的问题:缓存一致性,每个处理器都有自己的高速缓存,但是又共享同一主内存,这样就会导致各自的缓存数据不一致。
同时关于java内存模型还有基本的3个概念:

  • 可见性:可见性,是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的。也就是一个线程修改的结果。另一个线程马上就能看到
  • 原子性:原子是世界上的最小单位,具有不可分割性。比如 a=0;(a非long和double类型) 这个操作是不可分割的,那么我们说这个操作时原子操作。再比如:a++; 这个操作实际是a = a + 1,它不是一个原子操作,它是可分割的,它也有并发问题,即使你加上了volatile关键字;
  • 有序性:即程序执行的顺序按照代码的先后顺序执行,为什么这么说了,因为jvm还会对输入代码进行乱序执行(out-of-order Execution)优化,处理器会在计算之后将乱序执行的结果重组,也就是java虚拟机的即时编译器中也有指令重排序优化,关于有序性你还需要了解happens-before原则(先行发生原则);

happens-before原则(先行发生原则):

Java内存模型具备一些先天的“有序性”,即不需要通过任何手段就能够得到保证的有序性,这个通常也称为 happens-before 原则。如果两个操作的执行次序无法从happens-before原则推导出来,那么它们就不能保证它们的有序性,虚拟机可以随意地对它们进行重排序。
下面就来具体介绍下happens-before原则(先行发生原则):

程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作
锁定规则:一个unLock操作先行发生于后面对同一个锁额lock操作
volatile变量规则:对一个变量的写操作先行发生于后面对这个变量的读操作
传递规则:如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C
线程启动规则:Thread对象的start()方法先行发生于此线程的每个一个动作
线程中断规则:对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生
线程终结规则:线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行
对象终结规则:一个对象的初始化完成先行发生于他的finalize()方法的开始
介绍完这些后就可以进入本文第一个概念:

volatile:

当一个变量定义为 volatile 之后,将具备两种特性:

  • 将当前处理器缓存行的数据写回系统内存;
  • 这个写回内存的操作会使得其他CPU里缓存了该内存地址的数据无效,拿到该数据必须到主内存去拿。

在《深入理解java虚拟机》这样解释原理:
 “观察加入volatile关键字和没有加入volatile关键字时所生成的汇编代码发现,加入volatile关键字时,会多出一个lock前缀指令”

lock前缀指令实际上相当于一个内存屏障(也成内存栅栏),内存屏障会提供3个功能:

1)它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成;

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

3)如果是写操作,它会导致其他CPU中对应的缓存行无效。

你可能感兴趣的:(volatile实现原理)