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

前言

在java多线程中,有时候需要用到延迟初始化来降低初始化类和创建对象的开销。双重检查锁定是常见的延迟初始化技术,但是他是一个错误的方法。(其实最多的就是单例模式中会用到)

public class EndSingleton {
    private EndSingleton endSingleton;
    private EndSingleton(){}
    public EndSingleton getInstance(){
        if(endSingleton != null){
            return endSingleton;
        }
        synchronized(this){
            if (endSingleton != null){
                return endSingleton;
            }else {
                endSingleton = new EndSingleton();
                return endSingleton;
            }
        }
    }
}

这个应该是单例的最终版本,好像最新的还有一个用枚举实现的。我们目前不考虑。这个最终版本看上去很完美,但是其实存在问题,就是这个endSingleton不为null的时候,这个对象可能还没有完成初始化。

问题的根源

endSingleton = new EndSingleton()这一行代码可以拆解为如下的三行代码:
memory = allocate(); // 分配对象内存空间
ctorInstance(memory);// 初始化对象
instance = memory;//设置instance指向刚分配的内存地址
而上面的三行代码是有可能被重排序的
memory = allocate(); // 分配对象内存空间
instance = memory;//设置instance指向刚分配的内存地址
ctorInstance(memory);// 初始化对象
重排序之后,单线程的结果不改变,多线程的结果就会收到影响。我们之前介绍过,jmm内存模型保证是不改变程序的执行结果下(这个就是指的单线程),尽可能的通过重排序提升效率。多线程不能保证。知道问题,接下来就是怎么解决问题。

解决方案

解决方案的话,一种是给变量加上volatile关键字(voaltile的语义之前介绍过),另一种加上fianl(这是我自己根据final的语义推测的)。
还有一种就是基于类初始化,想要知道这个类初始化为嘛可以,我们首先的知道类的初始化条件(jvm系列有介绍)。
在首次发生下列任意一种情况时,一个类或接口T将立即被初始化。
1.T是一个类,而且一个T类型的实例被创建。
2.T是一个类,而且一个T中声明的一个静态方法被调用。
3…T中被声明的一个静态字段被赋值。
4…T声明的一个静态字段被调用,且这个字段不是一个常量字段。
5.T是一个顶级类,而且一个断言语句嵌套在T内部被执行
java语言中对于每一个接口和类,都有一个唯一的初始化锁LC与之对应,并且每个线程至少获取一次锁来确保这个类已经被初始化过了。这个就是给变量加上static。这个时候b去访问这个变量的时候首先要去获取锁初始化这个对象,然后发现a正在初始化,就会等待a初始化化完。然后发现初始化过了(详细点就是a加锁初始化,然后修该一个是否初始化过的状态值,然后释放锁,然后b获取到锁,判断时候初始化了,发现标志位表示初始化过了,就直接释放锁),就会直接读取整个对象值。这个也能保证b读之前a肯定是初始化完对象了。

总结

基于voaltile和类初始化两种方式都可以解决初始化的问题。但是相比较的化,类初始化可能要比volatile性能要更好点,但是在初始化实例字段的时候,就只能使用这个volatie。所以如果需要对实例化字段进行线程安全初始化的话,就需要volatile关键字,对静态字段进行线程安全初始化的话,就用基于类的初始化方案。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

mark---小鑫

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值