比如ImageLoader实例,里面包含线程池、缓存系统、网络请求等,很消耗资源!

饿汉模式

一开始就初始化

  1. public class Singleton {
  2. private static final Singleton instance = new Singleton();
  3. private Singleton() {
  4. }
  5. public static Singleton getInstance() {
  6. return instance;
  7. }
  8. }

懒汉模式

不要一开始就初始化,用到再初始化,节约资源

  1. public class Singleton {
  2. private static Singleton instance;
  3. private Singleton() {
  4. }
  5. public static synchronized Singleton getInstance() {
  6. if (instance == null)
  7. instance = new Singleton();
  8. return instance;
  9. }
  10. }

小问题是第一次加载时间稍慢;
大问题就是每次获取实例的时候都需要进行同步,造成不必要的同步开销。

Double CheckLock(DCL)

为了解决不必要的同步开销

  1. public class Singleton {
  2. private volatile static Singleton instance;
  3. private Singleton() {
  4. }
  5. public static Singleton getInstance() {
  6. if (instance == null) {
  7. synchronized (Singleton.class) {
  8. if (instance == null) {
  9. instance = new Singleton();
  10. }
  11. }
  12. }
  13. return instance;
  14. }
  15. }

这个有点意思哦,第一次判断null是为了不必要的开销,第二层是为了在null的情况下创建实例。

非原子操作 :instance = new Singleton(); 该行代码汇编后其实是三个操作:

  1. 给Singleton实例分配内存;
  2. 调用构造函数,初始化成员字段;
  3. 将instance指向分配好的内存空间(此时instance不为null)

问题就是第三部可能先于第二步执行,切换到其他线程时,instance不为空,但是Singleton还没有初始化,所以容易有问题

解决方案:
volatile ,jdk1.5之后加入的,为了保证对象每次都是从主内存中读取。

静态内部类

  1. public class Singleton {
  2. private Singleton() {
  3. }
  4. public static Singleton getInstance() {
  5. return SingletonHolder.instance;
  6. }
  7. private static class SingletonHolder {
  8. private static final Singleton instance = new Singleton();
  9. }
  10. }

第一次调用getInstance方法会导致虚拟机加载SingletonHolder类,确保线程安全,也能保证单例对象的唯一性,同时也延迟了单例的初始化。

枚举

  1. public enum SingletonEnum {
  2. INSTANCE;
  3. public void doSomething(){
  4. System.out.println("do sth.");
  5. }
  6. }

简单,高效,并且,反序列化的时候也不会重新生成新的实例。

定义

确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。

使用场景

确保某个类有且只有一个对象的场景,避免产生多个对象消耗过多的资源,或者某种类型的对象只应该有且只有一个。例如,创建一个对象需要消耗的资源过多,如要访问IO和数据库等资源,这时就要考虑使用单例模式。

Android中的应用

LayoutInflater类

总结

在客户端没有高并发的情况下选择哪种并没有太大的影响。

优点:

由于在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁地创建、销毁时,而且创建或销毁时性能又无法优化,单例模式的优势就非常明显了。
避免对资源的多重利用,例如写文件操作等。

缺点:

一般没有接口,扩展很困难。
单例对象如果持有 Context ,那么很容易引发内存泄漏,此时需要注意传递给单例对象的 Context 最好是 Application Context 。