java 单例 dcl_DCL单例到底需不需要volatile修饰

ba5c488eb944c3990780caf0f3f1b445.png

我们先来回答这个问题 DCL单例一定需要volatile修饰。

volatile有两个功能

内存可见

防止指令冲排序

这里主要考察volatile第二个功能。在介绍为什么DCL单例一定需要volatile修饰之前,我们先来看一下DCL单例和类实例化过程。

DCL单例

public class SingleExample {

private static volatile SingleExample singleExample = null;

private Long count = 99;

private SingleExample(){}

public static SingleExample getInstance(){

if(singleExample == null){

synchronized (SingleExample.class){

if(singleExample == null){

singleExample = new SingleExample();

}

}

}

return singleExample;

}

}

类实例化过程

这里以TestProcess为例来看一下Java对象实例化需要经过哪些步骤。

代码如下:

public class TestProcess {

public Integer num = 9;

public static void main(String[] args){

TestProcess testProcess = new TestProcess();

}

}

main方法执行完后我们来看一下生成的字节码

4c8390e3ee064d1d939a09c43aa26f8c.png

上面的字节码命令,我们只需要关注如下几行:

0 new #2

4 invokespecial #3 >

7 astore_1

下面我们来解释这三行的意思

//内存中分配空间,,同时为成员变量赋 '零值',即 num = null

命令1: 0 new #2

// 执行TestProcess类的构造方法

命令2: 4 invokespecial #3 >

// 将testProcess变量与内存生成的对象建立链接

命令3: 7 astore_1

场景解释

好了,解释完对象生成的关键这三步,我们回到主题上,我们来举例个场景来说明为什么DCL单例需要volatile修饰。

现在有两个线程

thread1 拿到了SingleExample类锁,正在实例化对象,并且刚执行完命令1 (此时count = null),将要执行命令2和命令3的时候,cpu发生了指令重排序,此时先执行了命令3。

此时 thread2,执行 if(singleExample == null),发现singleExample 不为null,,返回了singleExample对象。

对于thread2来说singleExample对象是没有意义的,因为该对象并未完成真正的初始化,成员变量count的值是null而不是99。如图所示:

3502f18840fe77db532511d19d0a3f80.png

至此DCL单例到底需不需要volatile修饰问题讲解结束,欢迎大家轻踩。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值