一、实现及时的可见性(volatile只能修饰成员变量);
修改的时候,会把数据更新到内存中,使其他线程的代码失效,重新从内存中读取数据;
总结来说两部分,修改被voliate修饰的成员变量,一个是更新内存中代码,一个就是使其他线程内的这个值失效;volatile这个关键字,就是在多线程的操作当中,保证我们变量的一个可见性,避免多线程操作下读取到脏数据
原理: 首先编译之后Java代码会被编译成字节码.class文件,在运行时会被加载到JVM中,JVM会将.class转换为具体的CPU执行指令,CPU加载这些指令逐条执行。
image.png
以多核CPU为例(两核),我们知道CPU的速度比内存要快得多,为了弥补这个性能差异,CPU内核都会有自己的高速缓存区,当内核运行的线程执行一段代码时,首先将这段代码的指令集进行缓存行填充到高速缓存,如果非volatil变量当CPU执行修改了此变量之后,会将修改后的值回写到高速缓存,然后再刷新到内存中。如果在刷新会内存之前,由于是共享变量,那么CORE2中的线程执行的代码也用到了这个变量,这是变量的值依然是旧的。
volatile关键字如何解决呢:首先被volatile关键字修饰的共享变量在转换成汇编语言时,会加上一个以lock为前缀的指令,当CPU发现这个指令时,立即做两件事:
1.将当前内核高速缓存行的数据立刻回写到内存;
2.使在其他内核里缓存了该内存地址的数据无效。
缓存一致性协议,也就是MESI协议,该解决缓存一致性的思路是:当CPU写数据时,如果发现操作的变量是共享变量,即在其他CPU中也存在该变量的副本,那么他会发出信号通知其他CPU将该变量的缓存行设置为无效状态。当其他CPU使用这个变量时,首先会去嗅探是否有对该变量更改的信号,当发现这个变量的缓存行已经无效时,会从新从内存中读取这个变量。
3.优缺点
volatile的好处:从底层实现原理我们可以发现,volatile是一种非锁机制,这种机制可以避免锁机制引起的线程上下文切换和调度问题。因此,volatile的执行成本比synchronized更低。
volatile的不足:使用volatile关键字,可以保证可见性,但是却不能保证原子操作。
二、防止指令重排序;https://blog.csdn.net/sinat_32873711/article/details/106619965
在一些方法内部,代码的执行顺序会被我们的cpu进行优化导致顺序会被打乱,但是结果不会受到影响;volatile就是 给那些被cpu优化所影响的代码,防止指令重排,使这个代码不被cpu优化所影响
———————————————————————————————————————————————————————————-
在编写程序的时候,JVM为了提高程序的执行效率,会对我们的程序进行优化,把经常需要被访问的变量存储在我们的缓存中,也就是存到我们的cpu的寄存器中,而避免直接去内存里读取,因为cpu从内存中读取数据,需要通过总线发送指令给内存,这个速度远比不上我们的cpu在我们的寄存器里直接读取数据,系统这个优化的好处非常明显,可以大大提高我们的程序的运行效率。但是当遇到多线程时,变量的值可能已经被别的线程改变了,而该缓存却无法察觉内存中的变化,从而造成我们的线程读取的数据和内存中的数据不一致,也就是所谓的脏数据。
例如:假设内存中有个变量A =1;线程1和线程2中均有该变量的一个缓存副本,但是线程2修改了该变量,那么此时内存中的A已经等于2了,但是线程1中的缓冲副本任然时1,那么这个时候我们线程1所缓冲的这个副本就是属于一个脏数据,如果使用volatile这个关键字进行修饰的话,系统每一次用到的这个变量的时候,会直接从内存中去取,而不会利用缓冲。需要注意的是,volite并不能保证我们操作的一个原子性,所以他并不能取代我们的synchronized的。此外,volatile这个关键字会阻止编译器对我们程序进行优化,除非很有必要,不然就要避免使用volatile这个关键字。
总结,volatile这个关键字,就是在多线程的操作当中,保证我们变量的一个可见性,避免多线程操作下读取到脏数据
image.png
volatile关键字是Java虚拟机提供的最轻量级的同步机制;
java内存模型允许编译器和处理器对指令重排序以提高运行性能,并且只会对不存在数据依赖性的指令重排序;
数据依赖性:写后读、读后写、写后写,前后没有数据依赖性
我们知道java在运行的时候有两个地方可能用到重排序,一个是编译器编译的的时候,一个是处理器运行的时候。
为啥要指令重排:https://segmentfault.com/a/1190000015032700
当使用voliate关键字修饰共享变量(实例变量、静态变量)时,它将具备两个特性:可见性和禁止指令重排序优化
image.png

1.可见性

  1. 变量被修改后,会立即保存在主存中,并清除工作内存中的值。
  2. 新值对于线程来说都是可见的。

    2.禁止指令重排序优化

  3. voliate禁止指令重排序是通过lock前缀指令实现的,lock前缀的指令相当于一个内存屏障,指令重排序时不能把后面的指令重排序到lock前缀指令之前,同时它会强制将对工作内存的修改操作立即写入主内存中。

    使用条件

  4. 对变量的写不依赖当前值,或者确保只有一个线程修改其值。

  5. 该变量没有包含在具有其他变量的等式。