单例模式

140 阅读3分钟

适用范围

DCL单例模式模式是为了在高并发环境中,提高数据的一致性,能够保证在并发环境中,保证一个类只有一个实例,提供一个全局访问点 DCL是double check Lock是一种方法,提供双重检查锁保证只能创建一种单例 单例模式实现方式

饿汉模式

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

这种方式在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快。这种方式基于类加载机制避免了多线程的同步问题,但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance显然没有达到懒加载的效果。

懒汉模式(Lazy init)

//线程不安全
public class Singleton{
  private static Singleton singleton;
  //将构造函数私有化,防止从外部实例化
  private Singleton() {};
  //静态方法获取实例
  public static Singleton getInstance(){
    if(singleton==null){
      singleton = new Singleton();
      return singleton;
    }
  }
}

这种单例模式在单线程能够保证唯一实例,但是在多线程下,就不能保证,在多线程下面,两个线程同时到达singleton==null判断的时候,不能保证实例唯一。懒汉模式申明了一个静态对象,在用户第一次调用时初始化,虽然节约了资源,但第一次加载时需要实例化,反映稍慢一些,而且在多线程不能正常工作

//线程安全
public class Singleton{
  private static Singleton singleton;
  //将构造函数私有化,防止从外部实例化
  private Singleton() {};
  //静态方法获取实例
  public static synchronized Singleton getInstance(){
    if(singleton==null){
      singleton = new Singleton();
      return singleton;
    }
  }
}

这种写法能够在多线程中很好的工作,但是每次调用getInstance方法时都需要进行同步,造成不必要的同步开销,而且大部分时候我们是用不到同步的,所以不建议用这种模式。

双重检查(DCL) 模式

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

在getSingleton方法中对singleton进行了两次判空,第一次是为了不必要的同步,第二次是在singleton等于null的情况下才创建实例。在这里用到了volatile关键字,在这里使用volatile会或多或少的影响性能,但考虑到程序的正确性,牺牲这点性能还是值得的。 DCL优点是资源利用率高,第一次执行getInstance时单例对象才被实例化,效率高。缺点是第一次加载时反应稍慢一些,在高并发环境下也有一定的缺陷,虽然发生的概率很小。DCL虽然在一定程度解决了资源的消耗和多余的同步,线程安全等问题,但是他还是在某些情况会出现失效的问题,也就是DCL失效,在《java并发编程实践》一书建议用静态内部类单例模式来替代DCL

静态内部类单例模式

public class Singleton{
  private Singleton singleton;
  public static Singleton getInstance(){
    return SingletonHolder.sInstance;
  }
  private static class SingletonHolder(){
    private static final Singleton sInstance = new Singleton();
  }
}

第一次加载Singleton类时并不会初始化sInstance,只有第一次调用getInstance方法时虚拟机加载SingletonHolder 并初始化sInstance ,这样不仅能确保线程安全也能保证Singleton类的唯一性,所以推荐使用静态内部类单例模式

使用容器实现单例模式

public class SingletonManager{
  private static Map<String,Object> objMap = new HashMap<String,Obeject>();
  private Singleton() {}
  public static void registerService(String key,Objinstance){
    if(objMap.contain(key)){
      objMap.put(key,Objinstance);
    }
  }
  public static ObjgetService(String key){
    return objMap.get(key);
  }
}

用SingletonManager 将多种的单例类统一管理,在使用时根据key获取对象对应类型的对象。这种方式使得我们可以管理多种类型的单例,并且在使用时可以通过统一的接口进行获取操作,降低了用户的使用成本,也对用户隐藏了具体实现,降低了耦合度.