Java并发编程:DCL双重检查锁 | volatile解决指令重排问题

在双重检查锁中,首先检查实例是否已经被创建,如果没有被创建,则进行同步操作,然后再次检查实例是否已经被创建。这种方式可以避免多个线程进入外层if,同时创建实例的问题,从而提高性能和效率。

多线程环境下还会存在创建实例未初始化问题,参考如下代码

public class Singleton4 implements Serializable {
    private Singleton4() {
        System.out.println("private Singleton4()");
    }

    private static volatile Singleton4 INSTANCE = null; // 可见性,有序性

    public static Singleton4 getInstance() {
        if (INSTANCE == null) {
            synchronized (Singleton4.class) {
                if (INSTANCE == null) {//第二次检查INSTANCE的原因是第一次创建INSTANCE的时候,2个线程可能都进入第一个if条件
                    INSTANCE = new Singleton4();
                }
            }
        }
        return INSTANCE;
    }

    public static void otherMethod() {
        System.out.println("otherMethod()");
    }
}

了解下singleton = new Singleton()这段代码其实不是原子性的操作,它至少分为以下3个步骤:

  1. 给singleton对象分配内存空间

  2. 调用Singleton类的构造函数等,初始化singleton对象

  3. 将singleton对象指向分配的内存空间,这步一旦执行了,那singleton对象就不等于null了

这里还需要知道一点,就是有时候JVM会为了优化,而做指令重排序的操作,这里的指令,指的是CPU层面的。

正常情况下,singleton = new Singleton()的步骤是按照1->2->3这种步骤进行的,但是一旦JVM做了指令重排序,那么顺序很可能编程1->3->2;

如果是这种顺序,可以发现,在3步骤执行完singleton对象就不等于null,但是它其实还没做步骤二的初始化工作,但是另一个线程进来时发现,singleton不等于null了,就这样把半成品的实例返回去,调用是会报错的。

在这里插入图片描述

所以,DCL使用volatile关键字,是为了禁止指令重排序,避免返回还没完成初始化的singleton对象,导致调用报错,也保证了线程的安全。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值