码迷,mamicode.com
首页 > 其他好文 > 详细

volatile关键字

时间:2018-08-07 01:34:44      阅读:172      评论:0      收藏:0      [点我收藏+]

标签:lock   创建   关键字   内存屏障   实现   解决方法   字节   完全   过期   

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关键字

标签:lock   创建   关键字   内存屏障   实现   解决方法   字节   完全   过期   

原文地址:https://www.cnblogs.com/joeCqupt/p/9434324.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!