问题:
如何保证一个类只被实例化一次?
某类对象希望保证只有一个实例,如数据库连接池(减少数据库连接的开销),如软件的全局配置信息(配置共享)等。
期望:
保证一个类仅有一个实例,并提供一个访问它的全局访问点。
实现:
思路:获取实例时,如果实例已经存在,则返回,否则创建。
实现-1 (懒汉)
优点:lazy loading,第一次调用才初始化,避免内存浪费。
缺点:必须加锁 synchronized 才能保证单例,加锁会影响效率。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class Singleton { private static Singleton uniqueInstance; private Singleton() {} public static synchronized Singleton getInstance() { if (uniqueInstance == null) { uniqueInstance = new Singleton(); } return uniqueInstance; } }
|
实现-2 (饿汉)
优点:没有加锁,提高执行效率。
缺点:类加载时就初始化,浪费内存。
1 2 3 4 5 6 7 8 9 10 11 12
| public class Singleton { // 基于classloder避免多线程同步问题,类装载的时候就实例化 private static Singleton instance = new Singleton(); private Singleton() { } public static Singleton getInstance() { return instance; } }
|
实现-3 (懒汉,DCL,double-checked locking)
采用双锁机制,安全且在多线程情况下能保持高性能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class Singleton { private volatile static Singleton singleton; private Singleton() { } public static Singleton getSingleton() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } }
|
实现-4 (静态内部类)
使用classloader实现,同实现-2的区别在于,使用SingletonHolder才会进行实例化,实现了lazy loading。
优点:DCL功能保证,实现简单。
缺点:只适用于静态域,实例域还是用DCL。
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class Singleton { private static class SingletonHolder { private static final Singleton INSTANCE = new Singleton(); } private Singleton() { } public static final Singleton getInstance() { return SingletonHolder.INSTANCE; } }
|
实现-5 (枚举,懒汉)
实现单例的最佳方法,lazy loading,线程安全,支持序列化机制,防止反序列化多次实例化。防止反射来调用私有的构造方法。
(枚举实例的创建是线程安全的。)
1 2 3 4 5 6 7 8
| public enum Singleton { INSTANCE; public void whateverMethod() { } } 调用: Elvis e = Elvis.INSTANCE
|
其他问题:
- 多个class loader时,会有多个实例。要保证多个Class Loader不会装载同一个Singleton
- 继承Singleton的子类可能有多实例的问题,杜绝继承Singleton
- 序列化会得到一个新的对象,破话了单例性。(序列化会通过反射调用无参数的构造方法创建一个新的对象。)
- 在反序列化时,会通过反射的方式调用要被反序列化的类的readResolve方法,所以只要在Singleton类中定义readResolve就可以解决该问题:(具体参见序列化源码)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| public class Singleton implements Serializable{ private volatile static Singleton singleton; private Singleton (){} public static Singleton getSingleton() { if (singleton == null) { synchronized (Singleton.class) { if (singleton == null) { singleton = new Singleton(); } } } return singleton; } private Object readResolve() { return singleton; } }
|
参考:
单例模式
深入浅出单实例SINGLETON设计模式
单例与序列化的那些事儿
理解Java对象序列化