单例模式的六种创建方式

2017-10-10  本文已影响35人  e小e

在使用java编码的过程中经常会去写单例模式,今天总结一下6种常见单例写法.

饿汉模式

public class HungerSingleton {
    private static HungerSingleton singleton = new HungerSingleton();
    private HungerSingleton() {
    }
    public static HungerSingleton getHungerSingletonInstance(){
        return singleton;
    }
}

评价:这种模式直接在类初始化的时候实例化单例,线程安全,但是没有延迟加载。

懒汉模式

public class LazySingleton {
    private static LazySingleton singleton;
    private LazySingleton() {
    }
    public static LazySingleton getLazySingletonInstance(){
        if (singleton == null){
            singleton = new LazySingleton();
        }
        return singleton;
    }
}

评价:这种模式在需要使用该对象的时候时候实例化单例,做到了延迟加载,节约内存,但是非线程安全。

懒汉线程安全模式

public class LazyThreadSafeSingleton {
    private static LazyThreadSafeSingleton singleton;
    private LazyThreadSafeSingleton() {
    }
    public synchronized static LazyThreadSafeSingleton getLazyThreadSafeSingletonInstance(){
        if (singleton == null){
            singleton = new LazyThreadSafeSingleton();
        }
        return singleton;
    }
    //或者
public static LazyThreadSafeSingleton getLazyThreadSafeSingletonInstance(){
        synchronized (LazyThreadSafeSingleton.class){
            if (singleton == null){
                singleton = new LazyThreadSafeSingleton();
            }
            return singleton;
        }
    }
}

评价:同时做到了线程安全,而且延迟加载,但是每次getLazyThreadSafeSingletonInstance都会使用synchronized关键字影响了效率.

DCL模式模式

public class DclSingleton {
    private static volatile DclSingleton mInstance = null;
    public static DclSingleton getInstance() {
        if (mInstance == null){
            synchronized (DclSingleton.class){
                if (mInstance == null){
                    mInstance = new DclSingleton();
                }
            }
        }
        return mInstance;
    }
}

评价:这里我们进行了双重check,第一次check没有synchronized关键字,比懒汉线程安全节约了效率,第二次check的时候加入了synchronized关键字再一次进行了一次是否为null的判断,这样保证在多线程并且mInstance都为null情况下进行重复创建。注意mInstance = new DclSingleton()这句代码,它实际上并不是一个原子操作,这条代码最终会编译成多条汇编指令,它大概会做三件事

  1. 给DclSingleton分配内存
  2. 调用DclSingleton构造函数,初始化成员字段
  3. 将mInstance对象指向分配的内存控件(此时mInstance就不是null了)
    上面2,3顺序是不能保证顺序执行的,也就是说顺序可能是123也有可能是132,也就是说在3执行完毕,2未执行完的情况,会存在其它线程调用getInstance()直接返回mInstance的情况。所以我们必须加上volatile关键字,保证不会出现132这种情况

静态内部类模式

public class StaticInnerSingleton {
    
    private StaticInnerSingleton(){
    }

    public static StaticInnerSingleton getInstance(){
        return StaticInnerHolder.sInstance;
    }

    public static class StaticInnerHolder{
        public static final StaticInnerSingleton sInstance = new StaticInnerSingleton();
    }
}

评价:这种单例模式简单,线程安全,并且延迟加载,推荐使用

枚举模式

public enum EnumSingleton {
    INSTANCE;
    public void doSomeThing(){
        System.out.println("do sth");
    }
}

评价:这种单例模式简单,线程安全

总结:任何单例模式都是将构造函数私有化,对外提供统一获取唯一实例的接口,在选择哪种模式要考虑线程安全,延迟加载因素。

上一篇 下一篇

猜你喜欢

热点阅读