也谈单例模式

单例模式(Singleton)也叫单态模式。

用途

有时候需要内存中仅有一个某对象就足够了,出现多个就会出现内存浪费甚至逻辑错误。典型的有数据库连接池等,一个连接池就够了。

常见实现

急加载(Eager Initialization)

Initialization On Demand Holder

public class Singleton {
    // 这个实例要不要不可变(final),一般不需要
    private static Singleton singleton = new Singleton();

    // 私有化构造器
    private Singleton() {
    }

    public static Singleton getInstance() {
        return singleton;
    }
}
DCL(Double-Checked Locking)

支持延迟加载

public class Singleton {
    private static Singleton singleton = null;

    private Singleton() {
    }

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

但是,DCL仍然会出现失败的情况,
DCL失败的原因:
关键在于这一句singleton = new Singleton();,并不是原子操作,大致会做下面几件事:

  • 开辟内存空间;

  • 引用指向内存空间(非null的一步);

  • 初始化内存空间;

但是,由于Java编译器允许处理器乱序执行(out-of-order),以及JDK1.5之前JMM(Java Memory Medel)中Cache、寄存器到主内存回写顺序的规定,初始化内存空间和连接引用与内存空间的执行顺序是不一定的,所以按照现在的顺序,有可能出现拿走一个为初始化的对象,造成很难fix的异常。
下面是优化版本。

双判空模式DCL with Volatile
public class Singleton {
    public static volatile Singleton singleton = null;

    public static Singleton getInstance() {
        Singleton s = singleton;
        if (singleton == null) {
            synchronized (Singleton.class) {
                s = singleton;
                if (singleton == null) {
                    singleton = s = new Singleton();
                }
            }
        }
        return singleton;
    }
}
静态内部私有类(Static Private Inner Class)
public class Singleton {
    private static class HelperSingleton {
        public static Singleton singleton = new Singleton();
    }

    public static Singleton getInstance() {
        return HelperSingleton.singleton;
    }
}
枚举单例模式
public enum EnumSingle {
    INSTANCE
}