美文网首页设计模式
设计模式-单例模式

设计模式-单例模式

作者: h2coder | 来源:发表于2020-10-14 00:03 被阅读0次

为什么要用单例模式?什么时候用?

  • 单例模式一般用于,创建时,需要创建比较多的对象,消耗比较多的内存时使用。
  • 当多处调用都需要同一个对象进行状态处理时,内存存在一个对象比较合理,而不是每次进行状态切换,就创建一个实例,或销毁之前的实例再重新创建一个。
  • 平时我们写代码时,例如推送、音频播放等,一般都会写一个Manager管理类,这些类包含了比较多的初始化操作,一般会做成单例。(例如Android的context.getSystemService(name),获取系统服务)。

怎样实现单例

  • 一般会将构造方法私有,外部无法使用new关键字进行创建实例。
  • 将实例静态成员化,类共享。
  • 提供一个静态方法(类函数),例如方法名为getInstance(),返回当前实例。内部进行非空判断,如果非Null则直接返回非空的唯一实例,如果为Null,则创建并将其赋值到静态的成员变量中。

实现单例的几种方式

  1. 饿汉式
  2. 懒汉式
  3. synchronized同步+volatile+Double Check
  4. 静态内部类
  5. 枚举

饿汉式

  • 一开始就初始化唯一的实例,缺点是如果该类构造时,如果存在过多的处理,会导致加载该类比较慢,可能会有性能问题。
public class SingleInstance {
  private static SingleInstance sInstance = new SingleInstance();
  
  private SingleInstance() {
  }
  
  public static SingleInstance getInstance() {
      return sInstance;
  }
}

懒汉式

  • 只有当第一次调用getInstance()时才实例化单例实例,下次再进来会进行判断不为null直接返回。(未考虑到多线程问题,多线程下依然会存在多个实例)
public class SingleInstance {
  private static SingleInstance sInstance;
  private SingleInstance() {
  }
  
  public static SingleInstance getInstance() {
      if (null == sInstance) {
          sInstance = new SingleInstance();
      }
      return sInstance;
  }
}

synchronized同步+volatile+Double Check

  • volatile关键字保证线程之间,单例变量的可见性,可见性是什么呢,意思是当其中一个线程去修改这个共享的单例变量时,其他线程能读到最后改变的值。
  • synchronized则是同步关键字,保证在判空条件语句时,只有一个线程能执行到。
public class SingleInstance {
  private static volatile SingleInstance sInstance;
  private SingleInstance() {
  }
  
  public static SingleInstance getInstance() {
      if (null == sInstance) {
          synchronized (SingleInstance.class) {
              if (null == sInstance) {
                  sInstance = new SingleInstance();
              }
          }
      }
      return sInstance;
  }
}

静态内部类

  • 利用Java中类的初始化在类加载时才触发,我们就可以将实例放在内部类中,getInstance()方法调用时,才初始化SingleInstanceHolder这个内部类,做到了延时初始化的作用。并且JVM在初始化类时,会加锁,确保了多线程的问题。(Java是多线程的,可能会出现多个线程同时去初始化一个类,会给Class做初始化锁处理)。
public class SingleInstance {
  private SingleInstance() {
  }
  
  public static SingleInstance getInstance() {
      return SingleInstanceHolder.sInstance;
  }
  
  private static class SingleInstanceHolder {
      private static SingleInstance sInstance = new SingleInstance();
  }
}

枚举

  • 枚举的构造方法本身就是私有的,保证外部不能创建实例。
  • 枚举和Class一样,可以拥有自己的成员变量和方法。
  • 枚举最后生成的类实质就是class,并且继承Enum类,在static静态代码块中初始化枚举实例。当一个Java类第一次被调用静态代码块时,都是线程安全的。
  • 基于Class的单例,在反射或反序列化下,依然不能保证单例。反射能去掉private访问修饰符,反序列化后的对象也是重新new出来的。而枚举则不是,枚举的序列化不是class中常规的序列化手段。
public enum SingleInstance {
    INSTANCE;
    public void doSomthing() {
        
    }
}

工厂方式

  • 像Android中的context.getSystemService(name),获取系统服务,多个服务单例,我们可以建立一个工厂类,提供一个IHelper接口(或者IManager),首先工厂单例,保证只有一个工厂实例,拥有静态的一个HashMap,提供注册、注销方法,以及查找方法,保证Map中只有一个Helper实例。
  • 因为这个Map是单例的,即使输入进来的对象不是作为静态变量,因为如此,间接成为单例。
public class HelperFactory {
    private static HashMap<Class<? extends IHelper>, IHelper> sHelpers;

    static {
        sHelpers = new HashMap<>();
    }

    private HelperFactory() {
    }

    private static final class SingleHolder {
        private static final HelperFactory INSTANCE = new HelperFactory();
    }

    public static HelperFactory getInstance() {
        return SingleHolder.INSTANCE;
    }

    public void registerHelper(Class<? extends IHelper> clazz, IHelper helper) {
        sHelpers.put(clazz, helper);
    }

    public void unregisterHeler(Class<? extends IHelper> clazz) {
        sHelpers.remove(clazz);
    }

    public <T extends IHelper> T findHelper(Class<T> clazz) {
        IHelper helper = sHelpers.get(clazz);
        if (helper != null) {
            return (T) helper;
        }
        throw new IllegalStateException("not find " + clazz.getName() + " Helper instance");
    }
}

Android源码中的单例模式

在ActivityManager中有这么一段代码,getService()方法,获取IActivityManagerSingleton对象,调用get()方法获取一个Singleton实例,Singleton的泛型为IActivityManager,那Singleton是什么类呢,听名字像是单例的意思,我们点击Singleton会发现进不去,因为这个类被标记为hide了,所以在android.jar中没有。

/**
 * @hide
 */
@UnsupportedAppUsage
public static IActivityManager getService() {
    return IActivityManagerSingleton.get();
}

@UnsupportedAppUsage
private static final Singleton<IActivityManager> IActivityManagerSingleton = new Singleton<IActivityManager>() {
    @Override
    protected IActivityManager create() {
        final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
        final IActivityManager am = IActivityManager.Stub.asInterface(b);
        return am;
    }
};

Singleton的代码路径:/frameworks/base/core/java/android/util/Singleton.java,发现Singleton的代码不多,也就10来行。

Singleton为一个抽象类,有一个create()抽象方法,被get()方法调用。当实例为null时,才调用create()创建并保存起来,有实例则直接返回,并且加上了synchronized同步关键字来处理多线程同步问题。

/**
 * Singleton helper class for lazily initialization.
 *
 * Modeled after frameworks/base/include/utils/Singleton.h
 *
 * @hide
 */
public abstract class Singleton<T> {
    private T mInstance;

    protected abstract T create();

    public final T get() {
        synchronized (this) {
            if (mInstance == null) {
                mInstance = create();
            }
            return mInstance;
        }
    }
}

相关文章

  • 单例模式Java篇

    单例设计模式- 饿汉式 单例设计模式 - 懒汉式 单例设计模式 - 懒汉式 - 多线程并发 单例设计模式 - 懒汉...

  • python中OOP的单例

    目录 单例设计模式 __new__ 方法 Python 中的单例 01. 单例设计模式 设计模式设计模式 是 前人...

  • 单例

    目标 单例设计模式 __new__ 方法 Python 中的单例 01. 单例设计模式 设计模式设计模式 是 前人...

  • 设计模式 - 单例模式

    设计模式 - 单例模式 什么是单例模式 单例模式属于创建型模式,是设计模式中比较简单的模式。在单例模式中,单一的类...

  • 设计模式

    常用的设计模式有,单例设计模式、观察者设计模式、工厂设计模式、装饰设计模式、代理设计模式,模板设计模式等等。 单例...

  • 2018-04-08php实战设计模式

    一、单例模式 单例模式是最经典的设计模式之一,到底什么是单例?单例模式适用场景是什么?单例模式如何设计?php中单...

  • python 单例

    仅用学习参考 目标 单例设计模式 __new__ 方法 Python 中的单例 01. 单例设计模式 设计模式设计...

  • 基础设计模式:单例模式+工厂模式+注册树模式

    基础设计模式:单例模式+工厂模式+注册树模式 单例模式: 通过提供自身共享实例的访问,单例设计模式用于限制特定对象...

  • 单例模式

    JAVA设计模式之单例模式 十种常用的设计模式 概念: java中单例模式是一种常见的设计模式,单例模式的写法...

  • 设计模式之单例模式

    单例设计模式全解析 在学习设计模式时,单例设计模式应该是学习的第一个设计模式,单例设计模式也是“公认”最简单的设计...

网友评论

    本文标题:设计模式-单例模式

    本文链接:https://www.haomeiwen.com/subject/jtompktx.html