Android 中的单例模式
概述
单例模式算是我接触设计模式这种思想所学习的第一个设计模式。记得刚入行时面试,面试官总是会让写一种单例模式的实现。这篇文章主要是来总结一下单例模式的几种实现以及每一种实现的优缺点,旨在领会每一种写法,真正明白他们的区别,免得以后尴尬。Mark。
定义
按照设计模式中的定义,Singleton模式的用途是"ensure a class has only one instance, and provide a global
point of access to it"
(确保每个类只有一个实例,并提供它的全局访问点)
故名思义,就是这个类在当次整个系统中只存在一个实例,所有的访问必须通过这个唯一的实例来进行调用。
按照上面的UML定义我们可以简单的实现,其实就是懒汉式的实现。关于这个优缺点请具体懒汉式实现,
六种写法
这里从网络借鉴学习了实现策略,总共有六种实现方法,每一种实现分析了优缺点。
主要从三个方面来评价每一种实现:
- 是否能够延迟加载/按需加载/懒加载 类的实例
- 是否是线程安全的
- 性能的高低
1.懒汉式
之所以称为懒汉式,是基于这个类的实例是否能够按需加载,也就是懒加载。
代码实现
/**
* 懒汉单例
* Created by smart on 2018/5/3.
*/
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {
}
public static LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
这种是最基本的实现,但是这种方式在多线程并发中是有问题的,不是唯一的实例。
假如A/B两个线程第一次同时访问这个getInstance方法获得实例,然而这个条件(instance == null)有可能同时都成立(并发执行),那么线程A和线程B会分别获得一个类的对象,这样的话,这个类的单例就失去意义了。
策略评价:
- 优点是可以实现延迟加载。在类初次加载的时候,由于只是声明了这个静态的对象,但不会自动初始化 instance对象,所以称为懒汉式
- 缺点是线程不安全。多线程并发无法保证唯一的实例
- 改进策略:需要保证线程安全
2.饿汉式
不管程序是否需要这个对象的实例,总是在类加载的时候就先创建好实例。如下实现,这个类在加载的时候就会初始化这个静态实例。理解起来就像不管一个人想不想吃东西都把吃的先买好,如同饿怕了一样。所以称为饿汉式的。
代码实现
/**
* 饿汉单例
* <p>
* Created by smart on 2018/5/3.
*/
public class HungrySingleton {
private static final HungrySingleton mInstance = new HungrySingleton();
private HungrySingleton() {
}
public static HungrySingleton getInstance() {
return mInstance;
}
}
策略评价:
- 线程安全。利用了类的加载机制,加载初始化静态变量,且被只会执行一次,且JVM会利用锁来同步多个线程对同一个类的初始化。这样就保证了构造方法只会调用一次。
- 不能懒加载。无论是否使用都会去初始化实例。
- 改进策略:需要懒加载,请看懒汉线程安全式
3.懒汉线程安全式
代码实现
/**
* 懒汉线程安全单例
* Created by smart on 2018/5/3.
*/
public class LazySafetySingleton {
private static LazySafetySingleton instance;
private LazySafetySingleton() {
}
public static synchronized LazySafetySingleton getInstance() {
if (instance == null) {
instance = new LazySafetySingleton();
}
return instance;
}
public static LazySafetySingleton getInstance1() {
synchronized (LazySafetySingleton.class) {
if (instance == null) {
instance = new LazySafetySingleton();
}
}
return instance;
}
}
策略评价:
- 线程安全。使用了synchronized同步锁。
- 懒加载。不会在类加载就初始化。
- 显然这种同步性能很低。由于使用了同步锁,所以每次调用getInstance都会进行同步。其实我们只想第一次(instance == null)进行同步,初始化成功后,以后每次都直接返回就行了。
- 改进策略:DCL
4.DCL
代码实现
/**
* 双重检查锁定 double-check-locking 单例
* Created by smart on 2018/5/3.
*/
public class DclSingleton {
// private static DclSingleton instance;// 0
private volatile static DclSingleton instance;
private DclSingleton() {
}
public static DclSingleton getInstance() {
if (instance == null) { //1 第一次检查
synchronized (DclSingleton.class) { //2 加锁
if (instance == null) { //3 第二次检查
instance = new DclSingleton(); //4 初始化
}
}
}
return instance;
}
}
策略评价:
- 这种策略看似解决了每次都需要同步的问题,但是由于 标记4处 instance = new DclSingleton(); 这个初始化是非原子性的操作。就是说这个在JVM中可能会分成几步执行,那么就会存在指令重排序的问题,所以需要继续改进 在标记0处添加 volatile 关键字。
- 改进后,懒加载
- 线程安全
当声明对象的引用为volatile后,初始化中的重排序,在多线程环境中将会被禁止,从而在根本上解决了问题。但是volatile在jdk1.5以后才能正确工作。
原因看volatile关键字的使用
5.静态内部类
代码实现
/**
* 静态内部类单例
* Created by smart on 2018/5/3.
*/
public class StaticInnerSingleton {
public static StaticInnerSingleton getInstance() {
return SingletonHolder.instance;
}
private StaticInnerSingleton() {
}
private static class SingletonHolder {
private static final StaticInnerSingleton instance = new StaticInnerSingleton();
}
}
策略评价:
- 这种静态内部类的实现,主要是运用类的加载机制来保证线程的安全(原因见懒汉式的线程安全优点)。同时这个内部类与外部类没有绑定关系,而且只有外部类调用的时候才会加载,所以能做到懒加载。
- 这也是一种被推荐的方式。
6.枚举
代码实现
/**
* 枚举单例
* Created by smart on 2018/5/3.
*/
public enum EnumSingleton {
INSTANCE;
public void doSomeThing() {
}
}
策略评价:
- 优点是线程安全、防止反射强行调用构造器,还提供了自动序列化机制,防止反序列化的时候创建新的对象。反之上面的其他几种方式都需要额外注意防止别人反射这个构造器创建实例、手动序列化来防止反序列化创建新的对象。
- 在Android内存优化的建议上尽量避免少用枚举,因为内存占用上是静态变量的两倍以上
Enums often require more than twice as much memory as static constants. You should strictly avoid using enums on Android.
使用场景
在Android中有很多地方用到了单例。
比如InputMethodManager
@SystemService(Context.INPUT_METHOD_SERVICE)
public final class InputMethodManager {
static final boolean DEBUG = false;
static final String TAG = "InputMethodManager";
static InputMethodManager sInstance;
/**
* Retrieve the global InputMethodManager instance, creating it if it
* doesn't already exist.
* @hide
*/
public static InputMethodManager getInstance() {
synchronized (InputMethodManager.class) {
if (sInstance == null) {
try {
sInstance = new InputMethodManager(Looper.getMainLooper());
} catch (ServiceNotFoundException e) {
throw new IllegalStateException(e);
}
}
return sInstance;
}
}
}
比如EventBus中
public class EventBus {
static volatile EventBus defaultInstance;
/** Convenience singleton for apps using a process-wide EventBus instance. */
public static EventBus getDefault() {
EventBus instance = defaultInstance;
if (instance == null) {
synchronized (EventBus.class) {
instance = EventBus.defaultInstance;
if (instance == null) {
instance = EventBus.defaultInstance = new EventBus();
}
}
}
return instance;
}
}
比如源码中的 android.util.Singleton
package android.util;
/**
* 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;
}
}
}
这是个单例的辅助类,把单例特点抽象出来,然后具体的实现交给抽象的create
方法。这样子类可以灵活选用这个单例创建方式。
系统中有很多处用到这个类,其中在ActivityManager
中获得AMS
的时候(严格说是获得AMS
在客户端的代理)就借用了这个辅助类(基于Android 8.0)
/**
* @hide
*/
public static IActivityManager getService() {
return IActivityManagerSingleton.get();
}
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;
}
};