单例设计模式解读

单例模式是应用最广的模式之一,在应用这个模式时,单例对象的类必须保证只有一个实例存在。许多时候整个系统只需要拥有一个全局对象,这样有利于协调系统整体行为。

基础知识

定义

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

UML

Singleton

要素

  • 构造函数不对外开放,一般为private.
  • 通过一个静态方法或者枚举返回单例类对象
  • 确保单例类的对象有且只有一个,尤其是多线程环境下
  • 确保单例类对象在反序列化时不会重新构建对象

单例模式的类型

单例设计模式分为:饿汉式、懒汉式、静态内部类、枚举、使用容器实现

饿汉式

public class Singleton{
   private static Singleton singleton = new Singleton();
   private Singleton(){} 
   public static Singleton getInstance(){  
        return singleton;  
    }  
}

备注: 饿汉式是线程安全的,因为在调用getInstance()方法之前,该实例就已经创建好了。

懒汉式

懒汉式之原型

public class Singleton {
  private static Singleton singleton=null;  
  private Singleton(){} 
   public static Singleton getInstance() {  
         if (singleton== null) {    
             singleton= new Singleton();  
         }    
        return singleton;  
    }   
}

备注: 这种懒汉式是线程不安全的,并发环境下很可能出现多个Singleton实例。

线程不安全的原因:在高并发的情况下,一个线程A执行到singleton = new Singleton(),但还没有获得对象(对象初始化是需要时间的),第二个线程B也在执行,执行到(singleton == null)判断,那么线程B获得判断条件也是为真,于是继续运行下去,线程A获得了一个对象,线程B也获得了一个对象,在内存中就出现两个对象。

线程安全的懒汉式

  • 在getInstance方法上加同步
public class Singleton{
  private static Singleton instance;
  private Singleton(){}
  // 为了在多线程情况下保证单例对象唯一性,添加了synchropized关键字
  public static synchropized Singleton getInstance(){
     if(instance==null){
       instance==new Singleton();
      }
     return instance;
  } 
}

备注 :单例只有在使用时才会被实例化,在一定程度上节约了资源;第一次加载时需要及时进行实例化,反应稍慢,最大问题是每次调用getInstance都进行同步,造成不必要的同步开销

  • 双重检验锁定(DCL)(推荐使用
public class Singleton{
   private volatile static Singleton sinstance=null;
   private Singleton(){}
   public static  Singleton getInstance(){
    // 对instance进行了两次判空:第一层判断主要是为了避免不必要的同步,第二层的判断则是为了在null的情况下创建实例
      if(sinstance==null){
         synchropized(Singleton.class){
             if(sinstance==null){
                 sinstance=new Singleton();
             }
        }
    }
    return sinstance;
  }
}

备注: 资源利用率高,第一次执行getInstance时单例对象才会被实例化,效率高;第一次加载时反应稍慢,也由于Java内存模式的原因偶尔失败。在高并发环境下也有一定的缺陷,虽然发生概率很小。

静态内部类

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被初始化。因此,第一次调用getInstance方法导致虚拟机加SingletonHolder类,这种方式不仅能够线程安全、保证单例对象的唯一性,同时也延迟了单例的实例化。

枚举

public enum SingletonEnum{
    INSTANCE;
    public void doSomething(){
        // 具体操作
    }
}

备注: 线程安全

容器实现单例模式

public class SingletonManager{
  private static Map<String,Object> objMap=new 
  HashMap<String,Object>();
  pricate SingletonManager() {}

  public static void registerService(String key,Object instance){
      if(!objMap.containsKey(key)){
          objMap.put(key);
      }
  }

  public static Object getService(String key){
      return objMap.get(key);
  }
}

备注:这种方式可以管理多种类型的单例,并且通过统一的接口进行获取操作,降低了用户的成本,也对用户隐藏了具体实现,降低了耦合度。

优缺点

优点
- 由于单例模式在内存中只有一个实例,减少了内存开支,特别是一个对象需要频繁地创建、销毁时,而且创建或销毁时性能无法优化,单例模式的优势就非常明显
- 由于单例模式只生成一个实例,所以,减少了系统的性能开销,当一个对象的产生需要比较多的资源时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,然后用永久驻留内存的方式来解决
- 单例模式可以避免对资源的多重占用,例如一个写文件操作,由于只有一个实例存在内存中,避免对同一个资源文件的同时写操作
- 单例模式可以在系统设置全局的访问点,优化和共享资源访问,例如,可以设计一个单例类,负责所有数据表的映射处理
缺点
- 单例模式一般没有接口,拓展很困难,若要拓展,除了修改代码基本上没有第二种途径可以实现
- 单例对象如果持有Context,那么很容易引发内存泄漏,此时需要注意传递给单例对象的Context最好是Application Context

适用场景

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

参考

  • Android源码设计模式解析与实战
  • 设计模式之禅

猜你喜欢

转载自blog.csdn.net/xufei5789651/article/details/79599183
今日推荐