volatile
JMM内存模型
volatile可见性底层是实现原理
底层实现主要是通过汇编lock前缀指令,它会锁定这块内存区域的缓存(缓存行锁定)并写回到主内存
lock指令:
1)将当前处理器缓存行的数据立即写回到系统内存。
2)这个写回内存的操作回引起其他CPU里面缓存了该内存地址的数据无效(MESI协议)
3)提供内存屏障功能,使lock前后指令不能重排序(lock前缀:lock指令不是 一种内存屏障,但是它能完成类似内存屏障的功能)
原子性操作流程:
先经过总线,只要经过总线就会触发线程1的监控就会将自己缓存的值失效,此时线程2还没有写入主内存,线程1失效,再次从主内存获取内容又获取到 false那怎么办?
加缓存锁,对内存的修改特别快,所以效率高,并不会等待很长时间,在sotre时候就开始加锁此时线程1在去主内存获取值发现加锁了就需要等待线程2 unlock才能获取到值,伪代码:Store (lock initFlag=true,wirte(主内存))unlock
volatile为什么不能保证原子性? ,存在数据丢失
Volatile保证可见性与有序性,但是不保证原子性,保证原子性则需要借助synchronized锁机制来实现。
线程1和线程2同时读取到值都是0,线程1开始执行++ ,线程1是值=1,还未write到主内存,线程2也开始++,线程1写入主内存,线程2失效,再次读取结果为1,线程2再次++,结果为2
volatile关键字增加了实例变量在多个线程之间的可见性,单volatile不支持原子性
volatile多个线程中可以感知实例变量被改了,并且可以获取最新的值使用,多线程读取共享便利是可以获取最新值使用。
volatile不处理数据的原子性,只是强制对数据的读写及时影响到住主内存
1.保证此变量对所有的线程的可见性,这里的“可见性”,如本文开头所述,当一个线程修改了这个变量的值,volatile 保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。但普通变量做不到这点,普通变量的值在线程间传递均需要通过主内存来完成。
2.禁止指令重排序优化。有volatile修饰的变量,赋值后多执行了一个“load addl $0x0, (%esp)”操作,这个操作相当于一个内存屏障(指令重排序时不能把后面的指令重排序到内存屏障之前的位置),只有一个CPU访问内存时,并不需要内存屏障;
原子性:如果你了解事务,那这个概念应该好理解。原子性通常指多个操作不存在只执行一部分的情况,如果全部执行完成那没毛病,如果只执行了一部分,那对不起,你得撤销(即事务中的回滚)已经执行的部分。
可见性:当多个线程访问同一个变量x时,线程1修改了变量x的值,线程1、线程2...线程n能够立即读取到线程1修改后的值。
有序性:即程序执行时按照代码书写的先后顺序执行。在Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。
指令重排:在不影响单线程程序执行结果的前提下,计算机为了最大限度的发挥机器性能,会对机器指令重排优化