美文网首页
懒汉单例的不安全性

懒汉单例的不安全性

作者: 做梦枯岛醒 | 来源:发表于2019-04-02 14:20 被阅读0次

    1. 普通

    简单的懒汉模式如下:

    public class Lazy {
        private static Lazy lazy;
    
        public static Lazy getInstance(){
            if(lazy == null){
                lazy = new Lazy();
            }
            return lazy;
        }
    }
    
    

    懒汉方式进行初始化的步骤就是在Jvm进行类加载的时候不进行对象创建,直到调用者调用getInstance()的时候才返回一个新的实例。

    这样做的好处很显然,刚才也有提到,JVM会进行延迟加载,不至于在初始化的时候完成很多操作。

    但是又有一个很显然的问题诞生了,那就是线程不安全

    考虑下面这样一个情景,当线程A与B同时访问getInstance()方法,假设A先到达,判断instance为null后准备创建对象,B也正好检查完准备创建,那么意想不到的事情就来了,A,B创建了两个不同的对象,而不是我们目标意义上的单例,由于这种线程执行的不确定性,导致这种写法是线程不安全的。

    2. 优化

    既然线程不安全,那么试试synchronized关键字。

    public class Lazy {
        private static Lazy lazy;
    
        public synchronized static Lazy getInstance(){
            if(lazy == null){
                lazy = new Lazy();
            }
            return lazy;
        }
    }
    

    加上同步关键字之后,线程变得安全,但是又随之而来一个问题,就是效率问题,我们知道同步的含义,同步是一种排队等待的执行方式,所以排队等待会浪费很多的时间,在多次调用中,并不能发挥很优的效率。

    3.再优化

    public class Lazy {
        private static Lazy lazy;
    
        public static Lazy getInstance(){
            if(lazy == null){
                synchronized (Lazy.class) {
                    if(lazy == null)
                        lazy = new Lazy();
                }
            }
            return lazy;
        }
    }
    

    使用同步关键字的效率低下原因在于,每个线程都要等待判断对象是否为空,但是实际上一次创建之后,不出意外后面的都是不为null的,白白浪费了很多等待时间,于是终极优化采用了同步语句块来实现同步。

    通过对Lazy.class 对象加锁,来实现创建对象的同步,所以判断是否为空的过程就省去了。

    但是当一群线程蜂拥而至的时候,判断lazy引用为null,就要争先恐后的去创建对象,这样当然不行,所以只能有一个精子(线程)进入卵细胞(临界区)【莫名其妙的开车】,当它进入临界区创建了一个对象后,其他的线程再进去就要判断lazy是否已经被创建。

    最终就得到了一个 线程安全,延迟加载,效率又高的懒汉模式。

    相关文章

      网友评论

          本文标题:懒汉单例的不安全性

          本文链接:https://www.haomeiwen.com/subject/mifgbqtx.html