volatile 可见性和有序性

例:

    private volatile static boolean initFlag = false;

底层的汇编结果:
在这里插入图片描述
这里我们看到会有一个lock;那什么是lock呢?

在修改内存操作时,使用 LOCK 前缀去调用加锁的读-修改-写操作(原子的)。这种
机制用于多处理器系统中处理器之间进行可靠的通讯,具体描述如下:
在 Pentium 和早期的 IA-32 处理器中,LOCK 前缀会使处理器执行当前指令时产生
一个 LOCK#信号,这总是引起显式总线锁定出现。《IA-32 架构软件开发人员手册 》

可以理解为CPU指令级的一种锁 引起显式总线锁定出现,可类比与两个cpu请求,对总线加锁,然后再读取内存进行修改;同时lock也能完成类似内存屏障的功能

那么什么是内存屏障呢?

内存屏障,又称内存栅栏,是一个CPU指令,它的作用有两个,一是保证特定操作的执行顺序,二是保证某些变量的内存可见性(利用该特性实现volatile的内存可见性)。由于编译器和处理器都能执行指令重排优化。如果在指令间插入一条Memory Barrier则会告诉编译器和CPU,不管什么指令都不能和这条Memory Barrier指令重排序,也就是说通过插入内存屏障禁止在内存屏障前后的指令执行重排序优化。Memory Barrier的另外一个作用是强制刷出各种CPU的缓存数据,因此任何CPU上的线程都能读取到这些数据的最新版本。总之,volatile变量正是通过内存屏障实现其在内存中的语义,即可见性和禁止重排优化。

早期这种lock方式太效率低了,后面就出现了缓存一致性协议MESI;
1.M 修改 (Modified) 、
2.E 独享、互斥(Exclusive)
3.S 共享 (Shared)
4.I 无效 (Invalid)
即出现对缓存行加锁,以及更改状态的方式,有总线来仲裁那个cpu加锁成功;

缓存行(Cache line):缓存存储数据的单元。

在这里插入图片描述
那volatile为什么不保证原子性?
一个变量i被volatile修饰,两个线程想对这个变量修改,都对其进行自增操作也就是i++,i++的过程可以分为三步,首先获取i的值,其次对i的值进行加1,最后将得到的新值写会到缓存中。
线程A首先得到了i的初始值100,但是还没来得及修改,就阻塞了,这时线程B开始了,它也得到了i的值,由于i的值未被修改,即使是被volatile修饰,主存的变量还没变化,那么线程B得到的值也是100,之后对其进行加1操作,得到101后,将新值写入到缓存中,再刷入主存中。根据可见性的原则,这个主存的值可以被其他线程可见。
问题来了,线程A已经读取到了i的值为100,也就是说读取的这个原子操作已经结束了,所以这个可见性来的有点晚,线程A阻塞结束后,继续将100这个值加1,得到101,再将值写到缓存,最后刷入主存,所以即便是volatile具有可见性,也不能保证对它修饰的变量具有原子性。

来源:https://blog.csdn.net/xdzhouxin/article/details/81236356

在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值