Loading

单例模式

date: 2020-09-28 16:09:00
updated: 2020-09-28 17:42:00

单例模式

1. 饿汉式

public class EHan {
    private static EHan instance = new EHan();
    private EHan(){}
    public static EHan getInstance(){
        return instance;
    }
}

优点:没有线程安全问题
缺点:在初始化时就创建好了,浪费内存空间

2. 懒汉式

2.1 线程不安全

public class LHan {
    private static LHan instance;
    private LHan(){}
    public static LHan getInstance(){
        if(instance == null){
            instance = new LHan();
        }
        return instance;
    }
}

优点:只有当用的时候才检查是否有实例,没有才创建
缺点:有线程安全与不安全两种,区别在于是否有 synchronized 关键字

2.2 线程安全

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

3. DCL

由于线程安全的懒汉式的 synchronized 是加在方法上的,如果该方法里还有其他的一些代码,会降低执行效率,加锁的粒度太粗,所以可以进而改写下面这个

3.1 懒汉式优化后的一种写法

public class LHan {
    private static LHan instance;
    private LHan(){}
    public static LHan getInstance(){
        if(instance == null){
            synchronized(this){
                instance = new LHan();
            }
        }
        return instance;
    }
}

但是这种写法也存在一定问题,当线程A在判断instancenull后停住了,此时还没有创建实例;线程B抢到了资源,发现instancenull,也会进入到代码块,于是A和B都会创建一个实例。

3.2 DCL 单例模式

Double Check Lock 两次检查,中间插入一个lock

public class LHan {
    private static volatile LHan instance; // 必须要加 volatile
    private LHan(){}
    public static LHan getInstance(){
        if(instance == null){ // 这一层判断是为了提高效率。锁竞争很耗费时间和效率,避免多个线程每一次getInstance都要进入到同步代码块
            synchronized(this){
                if(instance == null){
                    instance = new LHan();
                }
            }
        }
        return instance;
    }
}

有可能会存在,A在第一层判断结束后停住,B进入同步代码块,new一个对象实例,进行一定操作后,把instance置为null,然后A进入同步代码块,会再次new一个对象。解决:添加版本号。

4. 静态内部类

public class Singleton {
    private static class SingletionHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    private Singleton(){}
    public static final Singleton getInstance(){
        return SingletionHolder.INSTANCE;
    }

}

静态内部类的方式效果类似双检锁,但实现更简单。但这种方式只适用于静态域的情况,双检锁方式可在实例域需要延迟初始化时使用。

posted @ 2020-10-22 12:03  猫熊小才天  阅读(56)  评论(0编辑  收藏  举报