单例模式介绍
单例模式是使用最广泛的一种设计模式,在这种设计模式中,单例对象的类必须保证有且只有一种实力存在。在我们的应用中往往会存在这么一个全局对象,用来统一处理某种行为,如网络请求、数据缓存、图片加载等。像这种情况就没必要每次使用时都构造实例,而往往采用单例模式
应用场景
某种类型的对象有且只有一个对象,避免产生多个对象消耗更多的资源。
实现单例模式有如下关键点:
- 构造函数不对外开放,一般都是私有的。
- 对外提供一个静态的公共方法或枚举返回单例对象。
- 确保单例对象有且只有一个,如多线程情况下。
- 确保单例对象在反序列化时不会重新创建对象。
单例模式的实现方式
饿汉模式:这是最常见也是最简单的是想方式,在调用getInstance前,在声明静态对象时就进行了初始化。
public class Singleton {
private static Singleton instance=new Singleton();
private Singleton(){ }
public static Singleton getInstance(){
return instance;
}
}
懒汉模式:相对于饿汉不同的是,在用户第一次调用getInstance时才进行初始化,实现方式如下
public class Singleton {
private static Singleton instance=new Singleton();
private Singleton(){ }
public synchronized static Singleton getInstance(){
if(instance==null) {
instance=new Singleton();
}
return instance;
}
}
通过观察在getInstance方式中添加了synchronized关键字,也就是说getInstance是个同步方法,保证在多线程情况下保证了单例的唯一性。但是这里有个问题——即便instance已经初始化了,每次调用getInstance方法还是要进行同步,而方法同步都会消耗资源,造成不必要的开销。这也是懒汉单例模式存在的一个最大问题。
Double check Lock(双重校验):这种方式是在懒汉、饿汉式的基础的升级,既能够在需要时才初始化,又能保证线程安全,而且单例对象初始化后不需要进行同步锁。
public class Singleton {
private volatile static Singleton instance = null;
private Singleton(){ }
public static Singleton getInstance(){
if(instance==null) {
synchronized (Singleton.class){
if(instance==null) {
instance=new Singleton();
}
}
}
return instance;
}
}
DCL的特点在getInstance方法中的两次判空:
第一次判断主要是避免不必要的同步锁;
第二次判断是在null情况下创建实例;
在 instance=new Singleton();这条语句里,大致做了三件事:
- 给Singleton的实例分配内存
- 调用Singleton的构造函数
- 将instance对象指向分配的内存空间
如果按照正常的1、2、3步骤顺利执行,那我们能顺利获得实例对象。但是 编译器为了优化程序指令加快cpu处理速度,会有指令重排序——也就是说上面的三步可能会1、3、2顺序。如果线程A执行到3完毕、2未完成之前。被切换到线程B,这个时候instance因为在线程A内已经执行了3,这个实际instance则不会为空。所以线程B直接获取instance,在使用的时候就会报错。
这时只需将instance的定义改为private volatile static Singleton instance = null;就可以保证instance对象每次都要从主内存中获取。
静态内部类单例模式
当第一次加载Singleton类时并不会初始化instance,只有在第一次调用Singleton的getInstance方法才会导致instance被初始化。因此第一次调用getInstance方法会导致虚拟机加载SingletonHolderl类,同时保证了延迟加载、线程安全、对象的唯一性,所以推荐使用这种方式实现单例模式。
public class Singleton {
private Singleton(){ }
public static Singleton getInstance(){
return SingletonHolder.instance;
}
private static class SingletonHolder{
private static final Singleton instance=new Singleton();
}
}
枚举单例
public enum SingletonEnum {
INSTANCE;
public void doSomeThing(){
System.out.println("do sth");
}
}
容器单例模式
public class SingletonManager {
private static Map<String,Object> objectMap= new HashMap<>();
private SingletonManager(){}
public static void registerService(String key,Object instance){
objectMap.put(key,instance);
}
public static Object getService(String key){
return objectMap.get(key);
}
}