设计模式之单例模式

单例模式是应用最广的设计模式之一,也可能是大多数程序员所了解的第一种设计模式(是我)

介绍

使用场景

  • 很多时候我们需要一个全局对象,可以在各个activity中被调用,比如在Activity1中改变该对象的成员变量值,可以在ActivityB中得以使用
  • ↑太菜了只这么用过↑

作用/好处

  • 确保某个类的实例只有一个,并向整个系统提供这个实例
  • 全局单例对象有助于开发者协调整体行为
  • 对于创建时消耗大量资源的对象,可避免重复创建

实现

实现单例模式的关键点主要有以下几点

  • 构造函数不对外开放,使用private关键字
  • 通过静态方法或者枚举返回单例类的对象
  • 确保单例类的对象只有一个

饿汉式

public class Singleton {
   private final static Singleton instance = new Singleton();
   //私有化构造器
   private Singleton(){}
   //共有静态方法,对外暴露获取单例对象
   public static Singleton getInstance(){
       return instance;
   }
}

在声明静态对象时就已经初始化,如果初始化了却没有使用单例对象,则造成了不必要的内存开销。

懒汉式

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

与饿汉式不同的地方在于单例对象初始化的时机
并且getInstance()方法的synchronized关键字,保证了多线程情况下单例对象的唯一
相对的,每次调用getInstance()都会进行同步,消耗不必要的资源

双检锁/双重校验锁

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

在getInstance()方法中对instance进行了两次判空
第一次判断是为了判断不必要的同步
第二次判断是为了在多线程的情况下创建实例(比如线程1和线程2都判空,然后都尝试获取monitor创建instance,如果不加第二次判空,线程1创建完了instance,线程2又会创建一次)
同时instance对象前面还添加了volatile关键字,保证instance的可见性

静态内部类

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

第一次加载Singleton类时并不会初始化instance
只有在第一次调用getInstance()方法时会初始化
第一次调用getInstance()方法导致虚拟机加载SingletonHolder类,确保线程安全
也能确保单例对象的唯一性,同时也延迟了单例对象的实例化,所以推荐使用这种实现方式

枚举

public enum Singleton {
    INSTANCE;
    public void whateverMethod() {
    }
}

写法简单,线程安全,并且在任何情况下都是一个单例

使用容器实现单例模式

public class SingletonManager{
    private static Map<String,Object> objMap = new HashMap<>();
    private SingletonManager(){}
    public static void registerService(String key,Object instance){
        if(!objMap.containsKey(key)){
            objMap.put(key,instance);
        }
    }
    public static Object getService(String key){
        return objMap.get(key);
    }
}

程序的初始将多种单例类型注入到一个统一的管理类中,使用时根据Key获取对应类型的对象
这种方式使得我们可以管理多种类型的单例,并且在使用时可以统一的接口进行操作
降低了使用成本,同时隐藏了具体实现并降低了耦合度

之前只了解了下饿汉式,懒汉式,双重校验锁的单例模式写法,结果有次看到前辈写的代码里的枚举式单例模式硬是不认识,😓,由此想着把其他单例模式的写法也都了解下

posted @ 2020-04-04 22:28  ClearMoon  阅读(213)  评论(0)    收藏  举报