什么是单例模式?
单例模式(Singleton) 是最多见也最简单的设计模式,它的目的就是在全局只生成一个类的实例。java
什么场合用单例模式
应用中常常有多任务进行信息共享的需求,好比火车票售卖示例中,多个售票窗口其实共享一个票务池。若是一个票务池用一个类,那么这个类就只能用一个实例,不然多任务进行时会引发资源同步的问题。android
另外,频繁建立和销毁的对象也能够用一个固定的实例,这样的好处是节省内存重复建立和销毁的开销,提升程序的稳定性。web
面向对象的编程很容易实现单例模型,好比 Java、C++ 等等,本文以 Java 代码讲解。编程
单例模型的核心思想就是:私有化构造方法,只开放静态的获取方法。设计模式
单例模式的实现手段(Java)
饿汉式
// 饿汉式单例
public class SingletonHungry {
// 主动建立静态的私有实例
private static SingletonHungry singleton = new SingletonHungry();
// 私有的构造方法
private SingletonHungry(){}
//静态的公开的实例获取方法
public static SingletonHungry getInstance(){
return singleton;
}
}
饿汉式的例子一看就懂,无论三七二十一先建立了对象再说,不一样的进程经过 getInstance 获取的都是同一个对象,因此是线程安全的。缓存
但也有个很差的地方就是,若是某个类建立过程会消耗不少资源,但程序运行中没有调用过 getInstance 方法,那么就存在资源浪费的状况,若是一个系统存在很是多此类状况那么这个系统可能存在性能上的问题。安全
因此,咱们须要一种延迟加载的功能。多线程
懒汉式
public class SingletonLazy {
private static SingletonLazy instance;
private SingletonLazy (){}
public static SingletonLazy getInstance() {
if (instance == null) {
instance = new SingletonLazy();
}
return instance;
}
}
懒汉式在饿汉的基础上作了改进,先判断 instance 是否为空,若是为空则建立示例,不然直接返回。app
懒汉式作到了延迟加载,很是适合单线程。ide
但多线程下面会存在问题,若是多个线程同时调用 getInstance 方法,可能存在同时判断 instance 变量是否为空的状况,上面的代码中很容易致使重复建立多个实例,这违背了单例模式的目的。
通常而言,咱们会进行一些同步手段。
双检锁(DCL,double-checked locking)
public class SingletonDCL {
private volatile static SingletonDCL instance;
private SingletonDCL (){}
public static SingletonDCL getInstance() {
if (instance == null) {
synchronized (SingletonDCL.class) {
if (instance == null) {
instance = new SingletonDCL();
}
}
}
return instance;
}
}
DCL 进行两次 instance 的判断,而且利用了关键字 synchronized 。
试想当多个线程同时调用 getInstance 时,可能会存在同时判断 instance 为空的状况。
但由于 synchronized 关键字的存在,同一个时刻只能一个线程进入代码同步区域,其它线程会被阻塞。
被阻塞的线程从新得到进入代码临界区时,再次判断 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;
}
}
这种方法也能够作到延迟加载,可是它又不一样于饿汉式。
由于只有调用 getInstance 时,SingletonHolder 才会进行初始化。
以前作 Android 开发时,涉及到图片缓存加载的时候常常会看到一些开源组件有各种 ImageHolder 的代码,原理正是如此。
Android 源码中的单例模型
以 Android 系统版本为 9.0.0 代码为例,它的 framework 包中有一个 Singleton.java 文件。
package android.util;
/**
* Singleton helper class for lazily initialization.
*
* Modeled after frameworks/base/include/utils/Singleton.h
*
* @hide
*/
public abstract class Singleton {
private T mInstance;
protected abstract T create();
public final T get() {
synchronized (this) {
if (mInstance == null) {
mInstance = create();
}
return mInstance;
}
}
}
它是一个抽象类,而且支持泛型,从而支持模板化的形式建立任意类型的对象的单例。
源码很简单,经过 DCL 实现了懒加载。
咱们再看具体点。
xref: /frameworks/base/core/java/android/app/ActivityManager.java
/**
* @hide
*/
public static IActivityManager getService() {
return IActivityManagerSingleton.get();
}
private static final Singleton IActivityManagerSingleton =
new Singleton() {
@Override
protected IActivityManager create() {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am = IActivityManager.Stub.asInterface(b);
return am;
}
};
ActivityManager 是 Android 最核心的系统服务之一,它的代码有 4000 多行,上面是部分代码。它经过一个隐藏的 getService 调用而建立。
建立过程就是经过上面的 Singleton 实现。
从这个角度看,Android Framework 代码其实也不是很难是嘛,相信本身,你也能够写出不少相似代码出来。