趁着找工作的间隙给自己「充充电」。最近在看「Andriod设计模式」,在这之前对单例的理解比较浅显:知道单例模式可以保证一个场景有且只有一个对象,避免产生多个对象消耗资源;知道单例模式如果传context-activity参数容易引起内存泄漏,所以尽量使用context-application替代context-activity。但是真心不知道单例模式竟然有五花八门的写法:饿汉模式、懒汉模式、Double Check Lock 模式、静态内部类模式、枚举模式、容器模式。下面对这几种模式一一介绍,并分析其优缺点。
懒汉模式
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
缺点:这种模式多线程下无法保证对象唯一性
懒汉模式(同步)
public class Singleton {
private static Singleton instance;
private Singleton() {}
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
这个跟上面的区别是添加synchronized关键字,保证多线程情况下可以对象的唯一性,但是致命的缺点是:每次getInstance的时候都要同步,造成不必要的同步开销(99%情况下不需要同步),所以不建议使用。
饿汉模式
public class Singleton {
private static Singleton instance=new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}
优点是:写起来比较简单,而且不存在多线程同步问题,避免了synchronized所造成的性能问题;
缺点是:初始化类的时候就需要构造实例,(即便你还没有用到这个实例),因此在某些特定条件下会耗费内存。
Double Check Lock
public class Singleton {
private static Singleton mInstance = null;
private Singleton() {
}
public static Singleton getInstance() {
if (mInstance == null)
synchronized (Singleton.class) {
if (mInstance != null) {
mInstance = new Singleton();
}
}
return mInstance;
}
}
优点:资源利用率高,第一次执行getInstance时,对象才会被实例化,效率高。
缺点:第一次加载反应慢,在高并发或者低于JDK6版本下,不能保证对象唯一性。
静态模式(推荐使用)
public class Singleton {
private Singleton() {
}
public static Singleton getInstance() {
return SingletonHolder.sInstance;
}
private static class SingletonHolder{
private static final Singleton sInstance=new Singleton();
}
}
不仅可以保证线程安全,也能保证单例对象的唯一性,第一次加载Singleton不会初始化sInstance,第一次调用getInstance方法时才会初始话sInstance—推荐使用。
枚举单例
public enum Singleton {
INSTANCE;
public void whateverMethod() {
}
}
这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,但是这种方法对单价比较陌生,实际工作中很少看到有人这么写。
使用容器
public class SingleManager {
private static Map<String, Object> objectMap = new HashMap<String, Object>();
private SingleManager () {
}
public static void registerInstance(String key, Object mInstance) {
if (!objectMap.containsKey(key)) {
objectMap.put(key, mInstance);
}
}
public static Object getInstance(String key) {
return objectMap.get(key);
}
}
可以管理多种类型的单例(通过key方便获取),降低用户的使用成本。
总结
核心原来都是构造函数私有化,通过静态方法获取唯一实例,在获取的过程中保证线程安全、防止反序列化导致重新生成实例对象(高并发环境、JDK低版本、资源消耗)