上一篇文章中咱们介绍了 Synchronzied ,接下来咱们来介绍 volatile。安全
咱们知道 Java 内存模型有三大特性,有序性、可见性、原子性。bash
那么什么是有序性呢?Talk is cheap,show me the code 咱们直接来看代码。多线程
public static void main(String[] args) {
int num1=10;//代码1
int num2=5;//代码2
num1=num1-num2;//代码3
num2=num1*num1;//代码4
}
复制代码
这段代码的执行顺序必定是代码一、二、三、4 吗?这是不必定的,为何呢?由于可能会发生指令重排序。指令重排序就是在不影响程序最终结果的前提下,为了提高程序的执行效率,可能会对输入的代码顺序进行优化,形成执行顺序不必定按照代码书写顺序的一种形式。优化
咱们来看看指令重排序的定义,一个必要的条件就是不影响程序的执行结果,那么想一想看咱们的代码3和代码4的执行顺序可否调换顺序呢?答案天然是否认的,由于代码4须要依赖代码3的内容。spa
在单线程的状况下,重排序不会影响程序的运行结果,那么在多线程状况下呢?咱们来看一看。线程
//线程1:
context = loadContext(); //语句1
inited = true; //语句2
//线程2:
while(!inited ){
sleep()
}
doSomethingwithconfig(context);
复制代码
首先在线程1中先执行语句2,而后切换到线程2来执行。这个时候线程二认为初始化已经完成,就会进行接下来的操做,可是 context 并无初始化,因此就会致使代码出错。code
可见性是指当一个线程修改了共享变量后,其余线程可以当即得知这个修改。排序
咱们使用 volatile 关键字能够保证可见性,使得各个线程均可以读取到最新的值内存
原子性是指一个操做是不可中断的,要么所有执行成功要么所有执行失败,有着“同生共死”的感受。及时在多个线程一块儿执行的时候,一个操做一旦开始,就不会被其余线程所干扰。it
咱们来看一个最简单的例子,代码 i++ 是原子性操做吗?其实 i++ 能够分为3步来看待。
这其中任何一步发生均可能发生线程安全问题,因此 i ++ 不是原子性操做。
介绍完咱们的三大特性,咱们回过头再来讲 volatile , volatile 关键字能够作到可见性和有序性。也就是能够保证变量获取最新的值以及禁止指令重排序,可是它不能实现原子性,因此 volatile 的使用具备局限性,咱们简单地来总结一下它的使用场景。