首页 > 其他分享 >单例模式-双重校验锁

单例模式-双重校验锁

时间:2023-10-27 15:33:43浏览次数:25  
标签:Singleton 双重 校验 INSTANCE 线程 单例 new null

参考;https://blog.csdn.net/weixin_44471490/article/details/108929289

双重校验锁

饿汉模式是不需要加锁来保证单例的,而懒汉模式虽然节省了内存,但是却需要使用锁来保证单例,因此,双重校验锁就是懒汉模式的升级版本。

普通懒汉式

public class Singleton {
    
  	private static Singleton INSTANCE;
  
  	private Singleton() {}
  	
  	public static Singleton getInstance() {
      	if (INSTANCE == null) {
          	INSTANCE = new Singleton();
        }
      	return INSTANCE;
    }
}

单线程懒汉模式的问题

上面这段代码在单线程环境下没有问题,但是在多线程的情况下会产生线程安全问题。
在多个线程同时调用getInstance方法时,由于方法没有加锁,可能会出现以下情况
① 这些线程可能会创建多个对象
② 某个线程可能会得到一个未完全初始化的对象
对于 ① 的情况解释如下:

public static Singleton getInstance() {
    if (INSTANCE == null) {
        /**
         * 由于没有加锁,当线程A刚执行完if判断INSTANCE为null后还没来得及执行INSTANCE = new Singleton()
         * 此时线程B进来,if判断后INSTANCE为null,且执行完INSTANCE = new Singleton()
         * 然后,线程A接着执行,由于之前if判断INSTANCE为null,于是执行INSTANCE = new Singleton()重复创建了对象
         */
        INSTANCE = new Singleton();
    }
    return INSTANCE;
}

对于 ② 的情况解释如下:

public static Singleton getInstance() {
    if (INSTANCE == null) {
        /**
         * 由于没有加锁,当线程A刚执行完if判断INSTANCE为null后开始执行 INSTANCE = new Singleton()
         * 但是注意,new Singleton()这个操作在JVM层面不是一个原子操作
         *
         *(具体由三步组成:1.为INSTANCE分配内存空间;2.初始化INSTANCE;3.将INSTANCE指向分配的内存空间,
         * 且这三步在JVM层面有可能发生指令重排,导致实际执行顺序可能为1-3-2)
         *
         * 因为new操作不是原子化操作,因此,可能会出现线程A执行new Singleton()时发生指令重排的情况,
         * 导致实际执行顺序变为1-3-2,当执行完1-3还没来及执行2时(虽然还没执行2,但是对象的引用已经有了,
         * 只不过引用的是一个还没初始化的对象),此时线程B进来进行if判断后INSTANCE不为null,
         * 然后直接把线程A new到一半的对象返回了
         */
        INSTANCE = new Singleton();
    }
    return INSTANCE;
}

双重校验锁模式

public class Lock2Singleton {
  	private volatile static Lock2Singleton INSTANCE;    // 加 volatile
  
  	private Lock2Singleton() {}
  
  	public static Lock2Singleton getSingleton() {
      	if (INSTANCE == null) {                         // 双重校验:第一次校验
          	synchronized(Lock2Singleton.class) {        // 加 synchronized
              	if (INSTANCE == null) {                 // 双重校验:第二次校验
                  	INSTANCE = new Lock2Singleton();
                }
            }
        }
      	return INSTANCE;
    }
}

过程如下:
判断 INSTANCE 是否为null,检查变量是否被初始化(不去获得锁),如果已被初始化立即返回这个变量;

  • 不为null,直接返回,不用去竞争锁
  • 为null,获取锁,然后再次判断(虽然已经判断过,但是在第一个if和synchronized之间仍有可能被另外线程插入导致第一个if判断为null时,当进入同步代码块之后再次判断时已经不为null了,所以需要再次判断)
    • 是否为null
    • 为null,创建并返回
    • 不为null,直接返回

标签:Singleton,双重,校验,INSTANCE,线程,单例,new,null
From: https://www.cnblogs.com/hasome/p/17785257.html

相关文章

  • 完整教程:使用SPRING BOOT实现大文件断点续传及文件校验
    一、简介随着互联网的快速发展,大文件的传输成为了互联网应用的重要组成部分。然而,由于网络不稳定等因素的影响,大文件的传输经常会出现中断的情况,这时需要重新传输,导致传输效率低下。为了解决这个问题,可以实现大文件的断点续传功能。断点续传功能可以在传输中断后继续传输,而不需要从......
  • 设计模式—创建型模式之单例模式
    设计模式—创建型模式之单例模式介绍单例模式说明:一个单一的类,负责创建自己的对象,同时确保系统中只有单个对象被创建。单例模式特点:某个类只能有一个实例;(构造器私有)它必须自行创建这个实例;(自己编写实例化逻辑)它必须自行向整个系统提供这个实例;(对外提供实例化方法)单例模式......
  • 5_nest管道和数据校验
    管道Nest在路由处理器之前调用管道,然后管道接收发往路由处理器的参数验证输入数据,给输入数据添加字段管道有两个典型的用例:验证:评估输入数据,如果有效,则将其原样传递;否则,当数据不正确时抛出一个异常。转型:把输入数据转换为所需的格式(例如,从字符串到整型)。设置全局Valida......
  • 使用aop去实现token校验
    1.前言昨天去面试,被问懵的一个面试题,面试官看了一下简历,轻笑了一声原来你是用拦截器做的token校验啊,那么改用aop你怎么去做校验。我当时脑袋一篇空白。下面就写个小demo2.aop通知先回顾一下aop的通知,aop通知有五种分别如下前置通知:方法执行前通知后置通知:方法执行后通知......
  • Modbus协议详解4:RTU帧 & ASCII帧的差错校验
    前面已经分析过RTU帧和ASCII帧的报文区别,细心的朋友应该会发现在两种不同的报文传输模式下都有一个共同的组成部分——差错校验。这个差错校验在RTU模式和ASCII模式下也不是不相同的。看下面的对比:RTU模式的差错校验:ASCII模式的差错校验:总而言之,RTU模式下用的是CRC校验,ASCII模式下......
  • 2023-10-24 react+ts 遍历双重对象嵌套数组
    useEffect(()=>{if(value){constarr=value;for(constkinarr){console.log(k,arr[k]);arr[k].key=arr[k].id;arr[k].title=arr[k].name;for(constk2inarr[k].children){arr[k2]......
  • 校验 ChatGPT4 真实性的三个经典问题:快速区分 GPT3.5 与 GPT4,并提供免费测试网站
    现在已经有很多ChatGPT的套壳网站,以下分享验明GPT-4真身的三个经典问题,帮助你快速区分套壳网站背后到底用的是GPT-3.5还是GPT-4。大家可以在这个网站测试:https://ai.hxkj.vip,免登录可以问三条,登录之后无限制。咱们使用免登录的额度测试就已经够用了测试问题1:Whatist......
  • URPF---源地址校验
    uRPF技术:单播逆向路径转发(UnicastReversePathForwarding),其主要功能是防止基于源地址欺骗的网络攻击行为。在没有配置uRPF技术时,网络设备不检查数据包的源地址,只关心能不能到达目的地址。由此,产生了虚假源地址欺骗网络攻击行为,例如基于源地址欺骗的DOS攻击和DDOS攻击......
  • 在Delphi中使用正则表达式校验中文姓名
    usessystem.RegularExpressions;functionIsChineseName(constaName:string;constaMaxLength:Integer=10):Boolean;beginvarPattern:='^[\x{4E00}-\x{9FA5}]{2,'+aMaxLength.ToString+'}(·[\x{4E00}-\x{9FA5}]{2,'+aMaxLength.ToString......
  • 表单校验,常见的
    <el-formref="form":model="form":rules="rules"label-width="90px"class="readonly-field"></el-form>校验//密码校验constcheckoutPassword=(rule,value,callback)=>{constipRules=/^(?=......