• 双重校验锁实现单例模式


    public class Singleton {  
        private volatile static Singleton singleton;  
        private Singleton (){}  
        public static Singleton getSingleton() {  
        if (singleton == null) {  
            synchronized (Singleton.class) {  
            if (singleton == null) {  
                singleton = new Singleton();  
            }  
            }  
        }  
        return singleton;  
        }  
    }
    

    第一次判断singleton是否为null

    第一次判断是在Synchronized同步代码块外进行判断,由于单例模式只会创建一个实例,并通过getInstance方法返回singleton对象,所以,第一次判断,是为了在singleton对象已经创建的情况下,避免进入同步代码块,提升效率。

    第二次判断singleton是否为null

    第二次判断是为了避免以下情况的发生。
      (1)假设:线程A已经经过第一次判断,判断singleton=null,准备进入同步代码块.
      (2)此时线程B获得时间片,犹豫线程A并没有创建实例,所以,判断singleton仍然=null,所以线程B创建了实例singleton。
      (3)此时,线程A再次获得时间片,犹豫刚刚经过第一次判断singleton=null(不会重复判断),进入同步代码块,这个时候,我们如果不加入第二次判断的话,那么线程A又会创造一个实例singleton,就不满足我们的单例模式的要求,所以第二次判断是很有必要的。

    为什么要加Volatile关键字

    其实,上面两点比较好理解,第三点,既然有了Synchronized作为限制,为什么还要加入Volatile呢?

    首先,我们需要知道Volatile可以保证可见性和原子性,同时保证JVM对指令不会进行重排序。
      其次,这点也很关键,对象的创建不是一步完成的,是一个符合操作,需要3个指令。
      我们结合这一句代码来解释:

    singleton = new Singleton();

    指令1:获取singleton对象的内存地址
      指令2:初始化singleton对象
      指令3:将这块内存地址,指向引用变量singleton。

    那么,这样我们就比较好理解,为什么要加入Volatile变量了。由于Volatile禁止JVM对指令进行重排序。所以创建对象的过程仍然会按照指令1-2-3的有序执行。
      反之,如果没有Volatile关键字,假设线程A正常创建一个实例,那么指定执行的顺序可能2-1-3,当执行到指令1的时候,线程B执行getInstance方法,获取到的,可能是对象的一部分,或者是不正确的对象,程序可能就会报异常信息。

  • 相关阅读:
    Centos7安装Zabbix4.0步骤
    linux异常处理:selinux配置错误导致无法重启
    自学Python5.4-类 _init_方法
    自学Python5.3-类和对象的简单操作
    自学Linux Shell9.3-基于Red Hat系统工具包:RPM属性依赖的解决方式-YUM在线升级
    自学Linux Shell9.4-基于Red Hat系统工具包存在两种方式之二:源码包
    自学Linux Shell9.2-基于Red Hat系统工具包存在两种方式之一:RPM包
    JS 对象API之修改、删除对象的属性
    JS 对象API之判断自有属性、共有属性
    JS 对象API之判断父对象是否在子对象的原型链上
  • 原文地址:https://www.cnblogs.com/treasury/p/13122433.html
Copyright © 2020-2023  润新知