系统化的学习技术、研究技术而不是使用技术

单例的几种创建方式

更新于:2018/09/03 14:55:03 分类:Java技术

单例模式是Java设计模式里比较常用的一种创建型模式,它的创建方式有很多种,这里稍做总结。

饿汉模式(Eager Singleton)

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

懒汉模式(Lazy Singleton)

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

双重检查锁(Double-Checked Lock)

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

为了确保多线程模式下,仍然产生单个实例,双重检查锁模式在同步代码块内部和外部都判断了 instance == null。因为可能会有多个线程同时进入到同步代码块的if判断中,从而触发判断,防止重复创建实例。

这种写法看似十分完美,但却是有问题的。原因在于instance = new Singleton()并非原子性操作。

创建一个类的对象,分为三步:

  1. 分配对象的内存空间
  2. 初始化对象
  3. 将instance指向刚分配的内存地址

步骤2、3可能会被重排序,造成创建对象的顺序变了1-3-2。如果当为instance分配完内存后,这时另一个线程调用getInstance,判断为instance  != null,但实际上instance还没有完成初始化,也就是说会返回一个未初始化的对象,这就是问题所在,所以说双重检查锁是不完美的

这个问题在JDK5以上已经解决,办法是将instance声明为volatile来解决。

静态内部类(Initialization on Demand Holder)

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

这种方法使用内部类来做到延迟加载对象,在初始化这个内部类的时候,JLS(Java Language Sepcification)会保证这个类的线程安全。

这种写法最大的美在于,完全使用了Java虚拟机的机制进行同步保证,没有一个同步的关键字。

枚举模式

public enum SingletonEnum {
    INSTANCE;

    private  Singleton instance;
	SingletonEnum() {
        instance = new Singleton();
    }
    public Singleton getInstance() {return instance;}
}
class Singleton {} 

枚举模式是《Effective Java》里推荐的一种写法,不仅简单,而且保证了线程安全。

此方法无偿提供了序列化机制,绝对防止多次实例化,及时面对复杂的序列化或者反射攻击。单元素枚举类型已经成为实现Singleton的最佳方法。

参考资料

留言(0)

给我留言