多线程下的懒汉式(单例)问题以及解决办法

本文探讨了在多线程场景下,懒汉式单例模式可能出现的问题,即对象可能会被创建多次,违反单例原则。通过分析问题原因,提出使用DCL(Double-Check Locking)双端检查锁定机制来解决,同时解释了为什么要在if内部加锁而不是外部,并提到了指令重排可能导致的线程安全问题,通过使实例变量volatile来防止。总结了确保多线程下单例模式安全的方法。
摘要由CSDN通过智能技术生成

单线程下的懒汉模式

public class SingletonDemo {
	private static SingletonDemo instance=null;

    private SingletonDemo(){
        System.out.println(Thread.currentThread().getName()+"\t 我是构造方法");
    }

    public static SingletonDemo getInstance(){
         if(instance==null){
                    instance = new SingletonDemo();
                }
        return instance;
    }

    public static void main(String[] args) {
             System.out.println(SingletonDemo.getInstance()==SingletonDemo.getInstance());
    }
}

结果:无论如何输出,结果都是true,说明对象有且只创建了一次。

然而,当我们使用多个线程创建时

for (int i = 0; i <20 ; i++) {
    new Thread(()->{
        SingletonDemo.getInstance();
    },String.valueOf(i)).start();
  }

运行结果在这里插入图片描述
构造方法多次运行,说明对象被创建了多次,违背了单例(懒汉)模式的初衷:对象有且只创建一次。
分析:由于多线程下,有线程执行到箭头所指的地方,但还未new一个对象时,此时intance依旧为null,而此时有另外一个线程判断为instance为null也进入该方法,如此情况,造成对象多次创建。
在这里插入图片描述
解决办法:使用DCL双端检索机制

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

分析:在进if方法后进行再判断一次且加锁,每次只能有一个线程进去,这样就实现了单例模式
思考:为啥不直接在外面的if加锁呢?而要在if里面加锁再判断一次呢?
因为在外面加锁的话每次只有一个线程进去,而其他所有线程会被阻塞在方法开始处。
而在里面加锁再判断,因为有外层if的存在,提高运行的效率。

程序到这里还有潜在线程安全问题:
指令重排
分析:instance = new SingletonDemo();在初始化时,可以分为3步骤

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

而指令重排在保证数据依赖关系的情况下可能改变执行的顺序,而步骤2、3可能调换,执行顺序变为1、3、2。
当执行到1、3还没到2时,此时instance!=null,但是对象还没初始化完成,这时发生一件可怕的事情,有另外一个线程来读取instance,调用其方法,结果报空指针异常,哭晕在厕所。
解决:对象加入volatile关键字可以禁止指令重排
原理涉及:JMM内存模型,内存屏障,大家有兴趣可以了解了解。

这样,多线程单例模式才保证了安全性。

觉得有一点点帮助给个赞哟~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值