1.介绍
单例模式是应用最广泛的的模式之一,在应用这个模式时,必须保证单例对象的类只有一个实例。如在Android应用中,应该只有一个ImageLoader,ImageLoader含有线程池,缓存系统,网络请求,非常消耗资源,没有理由让它构造多个实例。这种不能自由构造对象的情况,就是单例模式的使用场景
2.单例模式的定义
确保一个类只有一个实例,而且自行实例化向整个系统提供这个实例。
3.使用场景
确保某个类有且只有一个实例对象场景,创建一个对象消耗的资源过多,如要访问IO和数据库等资源,这时就要考虑使用单例模式。
4.单例模式实现需要注意的几个关键点
3.1 构造函数不对外开放,一般为private。
3.2 通过一个静态方法或者枚举类返回单例类对象。
3.3 确保单例类的对象只有一个,特别是在多线程的情况下
3.4 确保单例类对象在反序列化时不会重新构建对象
5.单例模式实现的几种方式介绍
方式一:饿汉单例模式
public class SingletonTest {
private static final SingletonTest mSingletonTest = new SingletonTest();
private SingletonTest(){}
public static SingletonTest getInstance() {
return mSingletonTest;
}
}
方式二:懒汉单例模式
public class SingletonTest {
private static SingletonTest mSingletonTest = null;
private SingletonTest(){}
public static synchronized SingletonTest getInstance() {
if(mSingletonTest == null) {
mSingletonTest = new SingletonTest();
}
return mSingletonTest;
}
}
优点:懒汉模式是单例只有在使用的时候才会被实例化,在一定程度上节约了资源。
缺点:第一次加载需要及时实例化,反应较慢,最大的问题是每次调用getInstance方法都进行同步,造成了不必要的开销,一般不推荐使用。
方式三:Double Check Lock(DCL)实现
public class SingletonTest {
private static SingletonTest mSingletonTest = null;
private SingletonTest(){}
public static SingletonTest getInstance() {
if(mSingletonTest == null) {
synchronized (SingletonTest.class) {
if(mSingletonTest == null) {
mSingletonTest = new SingletonTest();
}
}
}
return mSingletonTest;
}
}
DCL优点:资源利用率高,第一次执行getInstance时,单例对象才会被实例化,效率高,能保证线程安全,调用getInstance不用每次同步
缺点:第一次加载时反应稍慢,在高并发环境下也有一定的缺陷,发生概率较小。
DCL模式是目前使用最多的单例实现方式,它能够在需要时才实例化单例对象,并且在绝多数情况下能保证单例对象的唯一性,除非你的代码在并发场景非常
复杂或者低于JDK 6版本,否则,这种方式一定能满足需求
方式四:静态内部类单例模式
public class SingletonTest {
private SingletonTest(){}
public static SingletonTest getInstance() {
return SingletonHolder.mSingletonTest;
}
private static class SingletonHolder {
private static final SingletonTest mSingletonTest = new SingletonTest();
}
}
第一次加载SingletonTest类时并不会初始化mSingletonTest,第一次调用getInstance方法会导致虚拟机加载SingletonHolder类,这种方式不仅能确保线程安全,也能保证对象的唯一性,同时也延迟了对象的实例化,是推荐使用的单例模式实现方式。
方式五:容器实现多个单例的管理
public class SingletonTest {
private static Map<String, Object> mMap = new HashMap<String, Object>();
private SingletonTest() {}
public static void registerService(String key, Object singleton) {
if(!mMap.containsKey(key)) {
mMap.put(key, singleton);
}
}
public static Object getService(String key) {
return mMap.get(key);
}
}
这种方式我们可以管理多种类型的单例,并且使用时通过统一的接口进行操作,降低了用户的使用成本,也对用户隐藏了使用细节,降低了耦合度。
上述几种创建单例的方式中,在一种情况下会出现重新创建对象的情况,那就反序列化。上述方法中如果想杜绝对象在反序列化时重新生成对象,那么必须加入如下方法:
private Object readResolve() throws ObjectStreamException {
return sInstance;
}