DCL到底需不需要加volatile

问题描述

        DCL全称Double Check Lock,描述的是单例模式中降低锁粒度后,使用双重检查的方式保证多线程下的数据一致性。

单例模式

1.饿汉式

        初始化类时使用静态变量修饰符定义对象并完成实例化,将默认构造方法设置为私有方法,并定义公共的对象获取方法返回静态对象实例。

package com.example.design_pattern.singleton;

public class HungrySingleton {
    private static HungrySingleton instance = new HungrySingleton();

    private HungrySingleton() {
        if (instance != null) {
            throw new RuntimeException("单例不允许创建多个实列");
        }
    }

    public static HungrySingleton getInstance() {
        return instance;
    }
}

2.懒加载

        初始化类时不进行实例化,只有在需要使用并获取该实例对象的时候才进行对象的实例化。该情况下为了保证实例的线程安全,则需要使用锁控制来控制对象的实例化过程。

        1.直接使用synchronized对获取实例对象的方法进行加锁

        2.缩小锁的范围,只在对象实例化的部分进行加锁操作,此时采用在锁对象前后双重检查的对象是否为空的方式来保证线程安全,这就是所谓的DCL。

DCL是否需要使用volatile

        实例化过程在字节码层面的正常顺序为:分配空间>>初始化赋值>>建立引用。在实际编译过程中,JIT可能会对字节码指令进行重新排序,排序后顺序为:分配空间>>建立引用>>初始化赋值。这种情况下DCL是无法保证线程安全的。volatile的用途是禁止指令重排序,从而保证实例对象的线程安全。

package com.example.design_pattern.singleton;

public class LazySingleton {
    /**
     * volatile防止指令重排导致的问题
     */
    private volatile static LazySingleton instance;

    private LazySingleton() {

    }

    public static LazySingleton getInstance() {
        if (instance == null) {
            synchronized (LazySingleton.class) {
                if (instance == null) {
                    //字节码层,JIT、CPU可能会对指令进行重排,
                    //正常顺序:1、分配空间,2、初始化赋值,3、建立引用
                    instance = new LazySingleton();
                }
            }
        }
        return instance;
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值