深究可见性,原子性,有序性的解决方案之volatile源码解析

   上节java内存模型(jmm)概念初探大致了解了由于cpu的快速发展,导致的越来越复杂的内存模型诞生,java内存模型相当于是底层内存模型的映射(实际并不是一一映射,但可以借鉴理解),也是衍生出并发三大特性:可见性,原子性,有序性,在多线程情况下这些特性也有多种方式可以保证,如volatile,synchronized关键字等,这一节从源码角度,分析volotile关键字。

  

   private static int int1;
     private static volatile int int2;public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10000; i++)
        {
            increase(i);
        }
        Thread.sleep(1000);
    }

    private static void increase(int i){
        int1= i+1;
        int2= i+2;
    }

首先看java代码,这里我用了2个变量,一个是volatile,另一个是普通的,我们看汇编打印:

 

 

 可以看到两者却别在于加了volatile关键字的多了一个lock 的指令,lock指令通过锁总线的方式(新的处理器以及改为缓存锁定的方式,就是锁住缓存行)会让紧跟在后面的指令变成原子操作,上面的lock addl $0x0其实就是lock后面又加了一个0,就是为了既不改变值,还能达到原子操作的效果。而且lock指令规定了要等前面的指令全部执行结束,然后把数据全部更新到主存,想一想这样是不是新的线程过来读到的状态都是最新的,但是之前就已经有缓存的怎么办呢?这个咱们的现在的一致性协议如MESI会保证其他Cache失效,从而到达数据一致的效果。lock指令也保证了前后指令相对lock来说有序。相当于是保证了可见性,原子性和有序性。但是为什么都说volatile又无法保证原子性呢,因为当你需要对volatile进行计算操作如volatile++,那就不是一个指令可以完成的了,而volatile显然不具备synchronized那样可以对整个临界区代码都能保证原子性的能力,所以一般说volatile不具备原子性。

  volatile保证可见性,有序性的核心就是lock指令,它会和MESI协议共同保证任意线程都可以读到最新的值,修改时也会及时同步主存。

 

posted @ 2022-03-18 13:40  吃肉不长肉的小灏哥  阅读(76)  评论(0编辑  收藏  举报