高并发(1)之Volatile关键字

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/MrLiar17/article/details/85062679

volatile修饰的共享变量,就具有了以下两点特性:

  • 1.保证了不同线程对该变量操作的内存可见性;--->可见性
  • 2.禁止指令重排序(内存屏障)----->有序性 

什么是内存可见性?

       在计算机执行程序的过程中,每条指令都是通过计算机cpu进行调度的,而指令执行过程中,势必涉及到读数据、写数据,而数据储存在主存当中,由于CPU执行的速度很快,而向主存读数据、写数据都很慢,如果每次都直接去主存读数据和写数据,势必会降低cpu的执行效率,因此cpu就有了高速缓存,当程序在运行过程中,会将运算需要的数据从主存复制一份到CPU的高速缓存当中,那么CPU进行计算时就可以直接从它的高速缓存读取数据和向其中写入数据,当运算结束之后,再将高速缓存中的数据刷新到主存当中。但在多线程中就会出现缓存一致性问题(如果多核CPU,可能靠近不同的cpu cache中,在写数据时就会出现数据的不一致性),最出名的就是Intel 的MESI协议,MESI协议保证了每个缓存中使用的共享变量的副本是一致的。它核心的思想是:当CPU写数据时,如果发现操作的变量是共享变量,即在其他线程中也存在该变量的副本,会发出信号通知其他线程将该变量的缓存行置为无效状态,因此当其他线程需要读取这个变量时,发现自己缓存中缓存该变量的缓存行是无效的,那么它就会从内存重新读取。

什么是指令重排序?

     指令重排序是JVM为了优化指令,提高程序运行效率,在不影响单线程程序执行结果的前提下,尽可能地提高并行度。

     有volatile修饰的变量,要满足数据依赖性(as-if-serial)【不管如何重排序(编译器与处理器为了提高并行度),(单线程)程序的结果不能被改变】,多了一个内存屏障(指令重排序时不能把后面的指令重排序到内存屏障之前的位置)

int a=1; //A指令
int b=2; //B指令
int c=a*b;//C指令

比如上面的A,B,C三条指令 ,为了加快运行速度,A,B指令可以同时进行,也同时可以进行指令重排,C指令依赖于A,B指令,所以C指令就不能重排序在A,B指令之前,要满足数据的依赖性(as -if-serival)和有序性。

volatile底层的实现机制?

       如果把加入volatile关键字的代码和未加入volatile关键字的代码都生成汇编代码,会发现加入volatile关键字的代码会多出一个lock前缀指令。

0x01a3de1d: movb $0x0,0x1104800(%esi);0x01a3de24: lock addl $0x0,(%esp);

      lock前缀指令实际相当于一个内存屏障,内存屏障分为两种:Load Barrier 和 Store Barrier即读屏障和写屏障。

内存屏障的功能:① 防止指令的重排序;②强制把写缓冲区/高速缓存中的脏数据等写回主内存,让其他缓存中相应的数据失效。

猜你喜欢

转载自blog.csdn.net/MrLiar17/article/details/85062679