volatile关键字

Posted joecqupt

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了volatile关键字相关的知识,希望对你有一定的参考价值。

volatile关键字

  • volatile关键字是轻量级的 synchronized

当变量被声明为 volatile的时候,在对volatile变量进行写操作时候,汇编指令会插入一个 Lock前缀指令,这个指令会引发两件事情。

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

    在多处理器下,为了保证各个处理器缓存是一致的,就会实现缓存一致性协议,每个处理器通过嗅探在总线上传播的数据来检查自己缓存的值是不是过期了。当处理器发现自己缓存行对应的内存地址被修改,就会将当前处理器的缓存行置为无效状态。

volatile关键字的内存语义

volatile的特性

  • 可见性,对于一个volatile变量的读,任意线程总是能看到对这个volatile变量最后的写入。
  • 原子性,对于任何一个volatile变量的读/写具有原子性(针对long和double),但是对于 volatile++不具有原子性。

volatile 的写-读 构建的 happens-before关系

从 jdk5 开始,volatile变量的写-读可以实现线程之间的通信。
从内存语义的角度来看,volatile写-读与锁的释放-获取具有相同的内存效果。volatile写和锁的释放具有相同的内存语义,volatile读与锁的获取有相同的内存语义。

volatile 内存语义的实现

为了实现volatile内存语义,JMM将限制编译器重排序和处理器重排序。JMM针对编译器制定的volatile重排序规则表。

能否重排序 第二个操作
第一个操作 普通读写 volatile读 volatile写
普通读写 NO
volatile读 NO NO NO
volatile写 NO NO

总结为一下3点:

  • 当第二个操作是 volatile写的时候,不能重排序,保证了第一个操作不能重排序到volatile写之后
  • 当第一个操作是 volatile读的时候,不能重排序,保证了第二个操作不能重排序到volatile读之前
  • 当第一个操作是volatile写,第二个操作是volatile读的时候,不能重排序。

为了实现volatile的内存语义,编译器在生成字节码的时候,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。

StoreLoad屏障

JSR-133 为什么要增强volatile的内存语义

旧的java内存模型中,虽然不允许volatile变量之间重排序,但是允许 volatile变量与普通变量重排序。
因此在旧的内存模型中,volatile的写-读没有锁的释放-获取所具有的语义。为了实现一种比锁更轻量级的线程之间的通信机制。JSR-133中增强了volatile的内存语义,严格限制编译器和处理器对volatile变量与普通变量的重排序。

双重检查锁定与延迟初始化

public class InstanceFactory {
    private static Instance instance;
    
    public static Instance getInstance(){
        if(instance==null){
            synchronized(InstanceFactory.class){
                if(instance==null){
                    instance =new Instance();
                }
            }
        }
        return instance;
    }
    static class Instance{
        //some code
    }
}

这个代码在 看上去是正确,有可能会得到一个为完全初始化成功的对象。

创建对象的代码可以分为3个部分:

memory = allocate(); //1.分配内存
ctorInstance(memory);//2.初始化对象
instance = memory;   //3.设置instance的内存地址

这个过程可以重排序为:

memory = allocate(); //1.分配内存
instance = memory;   //3.设置instance的内存地址

ctorInstance(memory);//2.初始化对象

所以当第二个判断 instance == null ,当判断对象不为空的时候,对对象进行访问可能会访问到一个为完全初始化成功的对象。

解决方法:1.禁止 操作2、3重排序
可以使用volatile变量修饰instance 禁止操作2、3重排序。 这个方案需要在jdk1.5或者更高的版本才行

public class InstanceFactory {
    private static volatile Instance instance;
    
    public static Instance getInstance(){
        if(instance==null){
            synchronized(InstanceFactory.class){
                if(instance==null){
                    instance =new Instance();
                }
            }
        }
        return instance;
    }
    static class Instance{
        //some code
    }
}



以上是关于volatile关键字的主要内容,如果未能解决你的问题,请参考以下文章

深度解析volatile关键字(保证够全面)❤❤

java volatile关键字

volatile关键字是什么意思

Java volatile关键字解惑

C语言 volatile 关键字在编译优化过程中有何作用

volatile关键字是不是能保证线程安全