volatile与单例模式

参考文献:volatile关键字的作用、原理

1 作用

  1. 保持内存可见性:所有线程都能看到共享数据的最新值。
  2. 防止指令重排序

2 实现

2.1 怎么实现内存可见性

(1)读取前先从内存刷新最新的值。
(2)写入后立即同步回内存中。

2.2 怎么防止指令重排

什么是指令重排:基于偏序关系的Happens-Before内存模型中,指令重排技术大大提高了程序执行效率。
指令序列中 插入内存屏障来禁止重排序。

例如:
下面是基于保守策略的JMM内存屏障插入策略:
在每个volatile写操作的前面插入一个StoreStore屏障。
在每个volatile写操作的后面插入一个StoreLoad屏障。
在每个volatile读操作的后面插入一个LoadLoad屏障。
在每个volatile读操作的后面插入一个LoadStore屏障。

3 单例模式

<1> 为了实现单例模式,需要再取对象实例前用if(null)判断是否存在。

class Singleton {
    private static Singleton instance;
    private Singleton(){}
    public static Singleton getInstance() {
        if ( instance == null ) { //这里存在竞态条件
            instance = new Singleton();
        }
        return instance;
    }
}

<2> 这样,多线程时也会出现多个实例。所以加同步代码块。

class Singleton {
    private static Singleton instance;
    private Singleton(){}
    public static Singleton getInstance() {
            synchronized (Singleton.class) {
                if ( instance == null ) {
                    instance = new Singleton();
                }
             }
        return instance;
    }
}

<3> 加了同步代码块,串行化了,效率太低了。所以使用双重检查锁。

class Singleton {
    private static Singleton instance;
    private Singleton(){}
    public static Singleton getInstance() {
        if ( instance == null ) { //当instance不为null时,仍可能指向一个“被部分初始化的对象”
            synchronized (Singleton.class) {
                if ( instance == null ) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

<4> 指令重排序
instance = new Singleton();
它并不是一个原子操作。事实上,它可以”抽象“为下面几条JVM指令:

memory = allocate();    //1:分配对象的内存空间
initInstance(memory);   //2:初始化对象
instance = memory;      //3:设置instance指向刚分配的内存地址

上面操作2依赖于操作1,但是操作3并不依赖于操作2,所以JVM可以以“优化”为目的对它们进行重排序,经过重排序后如下:

memory = allocate();    //1:分配对象的内存空间
instance = memory;      //3:设置instance指向刚分配的内存地址(此时对象还未初始化)
ctorInstance(memory);   //2:初始化对象

可以看到指令重排之后,操作 3 排在了操作 2 之前,即引用instance指向内存memory时,这段崭新的内存还没有初始化——即,引用instance指向了一个"被部分初始化的对象"。此时,如果另一个线程调用getInstance方法,由于instance已经指向了一块内存空间,从而if条件判为false,方法返回instance引用,用户得到了没有完成初始化的“半个”单例。

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

张之海

若有帮助,客官打赏一分吧

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值