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;
}