volatile是java中的关键字,主要保证了变量的可见性和有序性。说到这个我们就需要了解一下jvm的内存模型jmm
jvm内存:主内存(计算机物理内存)+工作内存(高速缓存区):
1.线程在工作内存获取变量值
2.工作内存获取主内存的值
3.修改后,存储在工作内存中
4.工作内存刷新数据到主内存中
注:当两个线程同时对一个共享变量操作时:例如:i=1
A线程获取i=1 拿到工作内存
B线程获取i=1 拿到工作内存
A线程执行i+1操作 此时i=2 然后刷新到主内存
B线程执行i+1操作 此时i=2 然后刷新到主内存
最终主内存中i=2
但是我们其实想要的正确结果是3。
上面说到的这个情况就是多线程并发导致的问题,我们知道在并发编程中,保证请求正确数据正确有3个原则:原子性,可见性,有序性
原子性:不可分割,也就是说不能分开执行,执行过程不可被打断。举个例子:项目中所说的事务就必须具备原子性。
可见性:当一个线程修改某个共享变量的值时,对于其他线程来说是立马可见的。
有序性:代码的执行顺序是有序的,不可改变的。
而volatile关键字就保证了可见性和有序性,但是不能保证原子性。
volatile主要操作的是共享变量:类的成员变量,类的静态变量
可见性:保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
有序性:修饰的变量不可被指令重排,保证有序性。
1)当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行;
2)在进行指令优化时,不能将在对volatile变量访问的语句放在其后面执行,也不能把volatile变量后面的语句放到其前面执行
volatile原理和实现机制:
主要是利用了内存屏障来做的处理;
1)它确保指令重排序时不会把其后面的指令排到内存屏障之前的位置,也不会把前面的指令排到内存屏障的后面;即在执行到内存屏障这句指令时,在它前面的操作已经全部完成;
2)它会强制将对工作缓存的修改操作立即写入主内存;
3)如果是写操作,它会导致其他CPU中对应的缓存行无效。
volatile使用场景:
为了满足并发三要素,一般对于共享资源我们都会采用synchronized关键字来处理,但是synchronized效率比较低,相当于是悲观锁意思,而且需要线程的上线文切换,在一些业务场景,使用volatile也可以达到效果,并且执行效率会高一些。
使用volatile修饰的变量一般必须满足下面两个条件:
1)对变量的写操作不依赖与变量的当前值
2)该变量不能被包含着有其他变量的不变式中(不参与与其他变量的计算)
所以这两个条件的意思也就是必须要保证原子性才可以,在使用volatile的时候保证程序的正确性。
一般用在状态的标记。