美文网首页
Java volatile 理解

Java volatile 理解

作者: Coffeelong | 来源:发表于2017-07-02 17:10 被阅读0次

    1. 现代CPU Cache结构

    多核 CPU Cache 结构
    1.1 缓存的主要作用

    现代多核CPU为了提升处理速度,都会将需要的数据从内存拷贝到各自的缓存中(L1,L2),然后在各自的缓存中对数据进行操作。

    1.2 缓存的作用范围

    L1,L2 是CPU中每个核私有的,用于备份各自所需要的数据。L3 Cache是CPU每个核共享的。

    1.3 缓存的写入模式

    写回(write-back)模式:各个核每次修改自己缓存中的数据后不会立即写回到内存,而是等到一定合适的时间才写回到内存。
    直写(write-through)模式:各个核每次修改自己缓存中的数据后会立即写回到内存。

    1.4 缓存的一致性

    无论哪种写入模式,试想,如果多个核都从内存缓存了一份相同的数据,而此时有一个核将自己缓存中的数据进行了修改,其他核缓存中的数据却依然是修改之前的数据,这就造成了各个核之间缓存数据的不一致,而我们所期望的是各个核缓存之间的数据可以同步,为了达到同步的目的,各个核的缓存需要共同遵守一份协议,保证修改共享数据的时候可以通知其他缓存,这就是CPU的缓存一致性协议。

    缓存一致性协议有多种,但是基本上都是基于MESI协议进行扩展的,详细的内容可以查阅相关文档了解。

    2. volatile的实现原理

    2.1 可见性

    可见性,即一个线程修改一个共享变量时,其他线程可以获取到修改后的值。JVM的实现中,volatile共享变量的写操作会向处理器发送一条Lock指令,声言使用直写模式,在CPU缓存一致性协议的保证下实现volitile共享变量的可见性。

    简单来讲,多核环境下,对volatile共享变量进行写操作会将缓存的结果直接写回到内存中,在缓存一致性协议下,其他核会对总线上传输的数据一直进行窥探,即监测其他核对缓存数据的操作。因此,volatile共享变量写回内存的操作会被其他核监测到,此时这些核会将自己缓存中的数据设为无效状态,当需要对这个数据进行修改操作的时候会重新从内存中读取,这样就达到了volatile修饰变量的可见性。

    2.1 原子性

    volatile修饰的变量无论是读还是写操作,都是具备原子性的,可以理解为使用了锁:

    volatile int value = 1;
    public void set(int value) {
       this.value = value;
    }
    public void get() {
       return this.value;
    }
    
    int value = 1;
    public synchronized void set(int value) {
       this.value = value;
    }
    public synchronized int get() {
       return this.value;
    }
    

    这意味着多线程环境下任何一个线程读取到的volatile修饰的共享变量都是当前的最新值,不会存在差异性。
    但是有一点需要注意,volatile只是保证了读/写的原子性,复合的volatile操作并不保证原子性,例如:

    volatile int value = 1
    public void set(int value) {
       this.value = value++;
    }
    

    因为value++这个操作可以分为三个步骤,首先,读取value的值,其次,
    进行自增操作,最后,将结果写入内存。需要知道这三个操作volatile并不能保证原子性,即只能保证读取到的value是最新值,如果此时该处理器读取value之后由于某些原因阻塞,而此时其他处理器刚好对value进行了修改,这个时候之前的处理器进行计算时还是使用之前读取到的value,这样就造成了错误的处理结果。

    相关文章

      网友评论

          本文标题:Java volatile 理解

          本文链接:https://www.haomeiwen.com/subject/byancxtx.html