volatile关键字解析

  • Volatile关键字有什么作用?

    Volatile关键字可以保证变量的可见性和有序性

  • 什么是可见性和有序性?
    要搞清楚什么是可见性必须要搞清楚java的内存模型
    java的内存模型是:所有的变量都存放在主存之中,然后每个线程都有一个自己单独的内存,叫工作内存,线程自己的的工作内存对自己来说是私有的,主存是所有线程都可以共享的内存。当线程要操作某个变量的时候,线程会从主存中拷贝一份这个变量到自己的私有工作内存中去,进行操作,操作完毕后再刷回到主存

    《volatile关键字解析》 在这里插入图片描述

    那么什么是可见性呢?

    volatile int i = 0;
    //线程A执行的代码
    public void increase(){
         i ++;
    }
    
    //线程B执行的代码
    public void decrease() {
         i  = i - 10;
    }
    

    就是在并发操作过程中,比如线程A 要操作i++,那么它要把i从主存中拷贝到自己的工作内存中去,然后在自己的工作内存中对i执行加1的操作,操作完毕后,在把加1后的i值写入到主存中,线程B也要对i进行操作,它会发现自己工作内存中的i值失效了,那么它必须再从主存中读取i值,拷贝到自己的工作内存中去,那么这个i值就是线程A改变后的值。
    所以可见性就是指当前线程对变量的操作结果对其他的线程是可见的,当前线程更改某个变量值后,会立马将变量的值刷新到主存,其他线程如果想操作这个变量,必须从主存中读取这个变量最新的值。
    变量加volatile可以保证变量的可见性。

    那什么是有序性呢?
    对于单线程,在不改变最终执行结果的前提下,虚拟机会对代码指令进行重排序,以优化执行速度。
    比如

     int i = 0;
     boolean a = false;
     
     i++;
     a = true;
    

    i++ 和 a = true 他们之间的操作互不影响,JVM在进行指令重排序的时候,有可能会把i++和a = true的执行顺序颠倒。在单线程中,这种指令重排序不会对程序造成什么影响。但是在多线程中,指令重排序可能会对最终的执行结果造成影响,使我们最终得到的结果并不是我们想要的结果,或者会引起异常。
    比如:

     //线程A
     context = loadContext();   //语句1
     inited = true;             //语句2
      
     //线程2:
     while(!inited ){
       sleep()
     }
     doSomethingwithconfig(context);
    

    如果线程A的指令进行重排序,语句2先于语句1进行执行,那么当执行完线程A的语句2后,A线程挂起,开始执行线程2的指令,那么就会直接执行doSomethingwithconfig(context)这条语句,而此时context还没有被赋值,导致context为空,有可能doSomethingwithconfig(context)会发生空指针异常或者得不到想要的结果。
    而volatile是怎么保证有序性的呢?
    volatile可以防止指令重排序,假如上面例子中inited被volatile修饰了,那么一定是语句1先于语句2执行,这样就会保证context的赋值先于inited被设置为true,这样就不会有异常行为产生了。

  • volatile能保证操作的原子性吗?

    volatile不能保证操作的原子性。

    操作的原子性是指这些操作是不能中断的,要么执行,要么不执行。
    比如:

    i = 10;
    i = j;
    i++;
    i = i + 2;
    

    上面只有 i = 10是原子性操作,i = j会有三步操作,即把j的值从主存拷贝到线程的工作内存,在把j的值赋值给i,然后再把i的值写回到主存中,虽然每一步都是原子性操作,但是合起来就不是原子性操作了。i++也有三步操作,从主存中拷贝i的值到线程工作内存,i在工作内存自增,再把改变后的i值写回到主存中。i = i+ 2,同样也有三步原子操做,合起来就不再是原子操作了,因为这三步原子操作可以在任何一步中断。

  • 那么怎么保证操作的原子性呢?

    可以使用Lock和synchronized、原子类(比如AtomicInteger)以及CAS操作保证操作的原子性

    Lock、synchronized以及CAS都是一种锁,Lock和synchronized是一种悲观锁,CAS是一种乐观锁,原子类保证操作原子性的原理是使用了CAS乐观锁。Lock使用灵活,可以在任何地方加锁解锁,Lock的加锁解锁是一对操作,现在Lock不怎么使用了。Synchronized是一种悲观锁,使用方便,可以使用类作为锁,也可以使用对象作为锁。可以给普通方法加锁,也可以给静态方法加锁,也可以给一个代码块加锁。CAS是一种乐观锁,乐观锁不会使没有获得锁的线程改变运行状态,而是会使它们一直重试,直到获取锁,这样线程就节省了状态转换的资源和时间消耗,效率比较高。不过现在synchronized也进行了优化,它的效率也比较高了。

以上就是volatile的解析,如有问题请指正。

    原文作者:元创造力
    原文地址: https://www.jianshu.com/p/17ee9ca9a9e9
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞