Java并发编程之volatile

上一篇文章中咱们介绍了 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 值
  • 对 i +1
  • 把新值赋给 i

这其中任何一步发生均可能发生线程安全问题,因此 i ++ 不是原子性操做。

介绍完咱们的三大特性,咱们回过头再来讲  volatile , volatile 关键字能够作到可见性和有序性。也就是能够保证变量获取最新的值以及禁止指令重排序,可是它不能实现原子性,因此 volatile 的使用具备局限性,咱们简单地来总结一下它的使用场景。

  • 对变量的写入操做不依赖当前的值,或者你能肯定只有单个线程可以更新变量的值
  • 该变量不会和其余状态变量一块儿归入不变性条件中
  • 在访问变量时不须要加锁
相关文章
相关标签/搜索