Java Volatile关键字解析

       在多线程并发编程中synchronized和volatile都扮演者重要角色。volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的“可见性”。

一、volatile的特性

    内存可见性:线程A对一个volatile变量的修改,对于其他线程是可见的,即线程获取volatile变量的值都是最新的。

二、实现原理相关说明

    内存屏障(memory barriers)是一组处理器指令,用于实现对内存操作的顺序限制。

    缓冲行(cache line)CPU高速缓存中可以分配的最小存储单位。处理器填写缓存行时,会加载整个缓存行。

    原子操作(atomic operations)不可中断的一个或一系列操作。    

三、如何保证内存可见性

    在X86处理器下通过工具获取JIT编译器生成的汇编指令来查看对volatile写操作时的情况。

    Java代码

    

instance = new Singleton();//instance是volatile变量

    转变为汇编代码

0x01a3deld:movb $0×0,0×1104800(%esi);0x01a3de24: lock addl $0×0,(%esp);

    有volatile变量修饰的共享变量进行写操作时会多一行汇编代码,其中有lock关键词,Lock前缀指令在多核处理器下会引发两件事情:

  1. 将当前处理器缓存行的数据写回到系统内存。
  2. 这个写回操作会使在其他CPU里缓存了该内存地址的数据无效。

    为了提高处理速度,处理器不直接和内存进行通信,而是先将系统内存的数据读到内部缓存后再进行操作,但是操作完不知道何时写到内存中。

                        Java Volatile关键字解析_第1张图片

    如果对声明了volatile变量进行写操作,JVM就会向处理器发送一个Lock前缀指令,将这个变量所在的缓存行的数据写回到系统内存。但其他处理器缓存的值还是旧的,所有在多处理器环境下,为保证各个处理器的缓存是一致的,就回实现缓存一致性协议,每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了,当处理器发现自己缓存行对应的内存地址被修改,就会将该缓存行设置为无效状态,当处理器对这个数据进行操作时就会重新从内存中读取数据到缓存中。

                            Java Volatile关键字解析_第2张图片

四、volatile实现原则

    1.Lock前缀指令会引起处理器缓存写回到内存。

    2.一个处理器的缓存写回到内存会导致其他处理器的缓存无效。

五、volatile的使用场景

    通过关键字sychronize可以防止多个线程进入同一段代码,在某些特定场景中,volatile相当于一个轻量级的sychronize,因为不会引起线程的上下文切换,但是使用volatile必须满足两个条件:

  1. 对变量的写操作不依赖当前值,如多线程下执行a++,是无法通过volatile保证结果准确性的;
  2. 该变量没有包含在具有其它变量的不变式中,这句话有点拗口,看代码比较直观。

    在项目中经常会用到volatile关键字的两个场景:

    1、状态标记量

    在高并发环境中,通过一个boolean类型的变量 flag,控制代码是否走其他逻辑。

public class Handler {
    private volatile flag; 
    public void setFlag(boolean flag) {
        this.flag = falag;
    }
    public void run() {
        if (flag) {
           //其他逻辑
        } else {
          //正常逻辑
        }
    }
}

用户的请求线程执行run方法,如果需要开启其他活动,可以通过后台设置,具体实现可以发送一个请求,调用其他方法并设置flag为true,由于flag是volatile修饰的,所以一经修改,其他线程都可以拿到flag的最新值,用户请求就可以执行其他逻辑了。

    2.双检锁/双重校验锁(DCL,即 double-checked locking)

    单例模式的一种实现方式,如果没有volatile关键字,会出现bug。 

  1.  volatile关键字可以保证jvm执行的一定的"有序性",在指令1和指令2执行完之前,指定3一定不会被执行。(禁止重排序)
  2.  保证了volatile变量被修改后立刻刷新会驻内存中。
public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getSingleton() {  
    if (singleton == null) {  
        synchronized (Singleton.class) {  
        if (singleton == null) {  
            singleton = new Singleton();  
        }  
        }  
    }  
    return singleton;  
    }  
}  


    


参考资料:

-《Java并发编程的艺术》 方腾飞 魏鹏 程晓明 著

- https://www.jianshu.com/p/195ae7c77afe 占小狼 

   

                

你可能感兴趣的:(Java基础学习)