比如ImageLoader实例,里面包含线程池、缓存系统、网络请求等,很消耗资源!
饿汉模式
一开始就初始化
public class Singleton {
private static final Singleton instance = new Singleton();
private Singleton() {
}
public static Singleton getInstance() {
return instance;
}
}
懒汉模式
不要一开始就初始化,用到再初始化,节约资源
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static synchronized Singleton getInstance() {
if (instance == null)
instance = new Singleton();
return instance;
}
}
小问题是第一次加载时间稍慢;
大问题就是每次获取实例的时候都需要进行同步,造成不必要的同步开销。
Double CheckLock(DCL)
为了解决不必要的同步开销
public class Singleton {
private volatile static Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
这个有点意思哦,第一次判断null是为了不必要的开销,第二层是为了在null的情况下创建实例。
非原子操作 :instance = new Singleton();
该行代码汇编后其实是三个操作:
- 给Singleton实例分配内存;
- 调用构造函数,初始化成员字段;
- 将instance指向分配好的内存空间(此时instance不为null)
问题就是第三部可能先于第二步执行,切换到其他线程时,instance不为空,但是Singleton还没有初始化,所以容易有问题
解决方案:
volatile ,jdk1.5之后加入的,为了保证对象每次都是从主内存中读取。
静态内部类
public class Singleton {
private Singleton() {
}
public static Singleton getInstance() {
return SingletonHolder.instance;
}
private static class SingletonHolder {
private static final Singleton instance = new Singleton();
}
}
第一次调用getInstance方法会导致虚拟机加载SingletonHolder类,确保线程安全,也能保证单例对象的唯一性,同时也延迟了单例的初始化。
枚举
public enum SingletonEnum {
INSTANCE;
public void doSomething(){
System.out.println("do sth.");
}
}
定义
确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。
使用场景
确保某个类有且只有一个对象的场景,避免产生多个对象消耗过多的资源,或者某种类型的对象只应该有且只有一个。例如,创建一个对象需要消耗的资源过多,如要访问IO和数据库等资源,这时就要考虑使用单例模式。
Android中的应用
总结
优点:
由于在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁地创建、销毁时,而且创建或销毁时性能又无法优化,单例模式的优势就非常明显了。
避免对资源的多重利用,例如写文件操作等。
缺点:
一般没有接口,扩展很困难。
单例对象如果持有 Context ,那么很容易引发内存泄漏,此时需要注意传递给单例对象的 Context 最好是 Application Context 。