应用最广的模式-单例模式

单例模式

饿汉式:

public class HungrySingleton {
  //构造函数私有
  private HungrySingleton(){
  }
  private static final HungrySingleton HUNGRY_SINGLETON = new HungrySingleton();
  //对外暴露单例对象接口
  public static HungrySingleton getInstance(){
    return HUNGRY_SINGLETON;
  }
}

优点:

实现简单,避免了线程同步问题。

缺点:

当类装载的时候就会创建类实例,不管你用不用,先创建出来,浪费资源

懒汉式

public class LazySingleton {
  private static LazySingleton lazySingleton;

  private LazySingleton() {
  }

  //用synchronized修饰同步代码块, 保证多线程下生成唯一的单例
  public static synchronized LazySingleton LazySingle() {
    if (lazySingleton == null) {
      lazySingleton = new LazySingleton();
    }
    return lazySingleton;
  }
}

优点:

单例只有在使用时,才会被实例化,在一定程度上节约了资源

缺点:

第一次调用时需要及时实例化,反应稍微慢点, 最大的问题是每次调用getInstance都进行同步,造成不必要的同步开销。这种模式一般不建议使用。

懒汉式之DCL模式(double check lock)

public class DCLSingleton {
  private static DCLSingleton dclSingleton;
  private DCLSingleton(){}
  public static DCLSingleton getInstance(){
    //第一个null判断是为了判断dclSingle是否为空,避免不必要的同步
    if(dclSingleton == null){
      synchronized (DCLSingleton.class){
        //第二个null判断是为了在null的情况下创建实例
        if(dclSingleton == null){
          dclSingleton = new DCLSingleton();
        }
      }
    }
    return dclSingleton;
  }
}

优点:

资源利用率高,第一次执行getInstance时单例对象才会实例化, 效率高。

缺点:

第一次反应稍慢,也由于java内存模型的原因偶尔会失败。在高并发环境下也有一定的缺陷, 虽然发生概率很小。
DCL模式是使用最多的单例实现方式,它能够在需要时才实例化单例对象,并且能够在绝大多数场景下保证单例对象的唯一性,除非你的代码在并发场景比较复杂或者低于1.6版本下使用,否则,这种方式一般能够满足要求。

懒汉式之静态内部类单例模式(IoDH)

public class StaticInnerSingle {

  private StaticInnerSingle(){}
  public static StaticInnerSingle getInstance(){
    return SingletonHolder.staticInnerSingle;
  }
  //静态内部类
  private static class SingletonHolder{
    private  static  final StaticInnerSingle staticInnerSingle = new StaticInnerSingle();
  }
  /*
  * 当第一次加载StaticInnerSingle类时并不会初始化 StaticInnerSingle,只有在第一次调用getInstance方法时
  * 才会导致StaticInnerSingle初始化
  * 知识点
  * */
}

  /*
  * 当第一次加载StaticInnerSingle类时并不会初始化 StaticInnerSingle,只有在第一次调用getInstance方法时
  * 才会导致StaticInnerSingle初始化
  * 知识点
  *  1.加载一个类时,其内部类不会同时被加载。一个类被加载,当且仅当其某个静态成员(静态域、构造器、静态方法等)被调用时发生
  * */
}

优点:这种方式不仅能够保证线程安全,也能够保证单例对象的唯一性,同时也延迟加载了单例的实例化,所以这是推荐使用的单例模式
缺点:是与编程语言本身的特性相关,很多面向对象语言不支持IoDH

枚举单例

public enum EnumSingleton {
  INSTANCE
  public void dosomething(){
  }
}

优点:

写法简单,线程安全,最大的优点是反序列化时也是单例,其他的单例模式在反序列化不能保证一个单例。

缺点:

当想实例化一个单例类的时候,必须要记住使用相应的获取对象的方法,而不是使用new,可能会给其他开发人员造成困扰,特别是看不到源码的时候。

使用容器模式实现单例模式

public class SingletonManger {
  private static Map<String, Object> objectMap = new HashMap<String, Object>();
  private SingletonManger(){}
  public static void registerService(String key, Object instance){
    if(!objectMap.containsKey(key)){
      objectMap.put(key, instance);
    }
  }
  public static Object getService(String key){
    return objectMap.get(key);
  }
}
/**
*在程序的初始,将多种单例类型注入到一个统一的管理类中,在使用时根据key获取对象对应类型的对象。这种方式使得我们可以管理多种类型的单例,并且在使用时可以通过统一的接口进行获取操作,降低了用户的使用成本,也对用隐藏了具体实现,降低了耦合度。
*/

优点

降低了用户的使用成本,也对用隐藏了具体实现,降低了耦合度。

缺点

这种模式在源码中更容易常见, 在Android中,在虚拟机第一次加载comtextImpl类时会注册各种
ServiceFather,将这些服务以键值对的形式存储在一个HashMap中,用户使用时根据key来获取
对应的ServieFetcher,然后通过ServiceFetcher对象的getService函数来获取具体服务对
象。

单例模式的核心

不管以哪种形式实现单例模式,其核心原理都是将构造函数私有化,并且通过静态方法获取一个唯一
实例,在这个获去的过程必须保证线程的安全、防止反序列化导致重新生成实例对象等问题。
选择哪种实现方式取决于项目本身,如是否是复杂的开发环境,JDK版本是否过低、
单例对象的资源等

单例模式github地址

https://github.com/binleo731/modeExample.git

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值