定义及使用场景
定义:确保某个类只有一个实例,而且自行实例化提供给外部使用。
使用场景:某个类型的对象实例应该有且只有一个,或者避免创建多个对象实例消耗过多的资源时。如:网络库,访问IO,数据库时要考虑单例模式。
单例模式的实现方式
1.饿汉式
优点:简单,线程安全
缺点:在类加载的时候就创建,不管需不需要都会创建,不能传参
适用场景:单例的创建开销小
java:
public class HungrySingleton {
//在类加载的时候创建单例
private static HungrySingleton instance = new HungrySingleton();
//私有化构造方法,避免外部调用
private HungrySingleton() {
}
//对外提供方法题获取单例对象
public static HungrySingleton getInstance() {
return instance;
}
/**
* 反序列化也没有问题
*/
private Object readResolve() {
return instance;
}
}
kotlin:
//kotlin中没有static关键字,直接用object声明类就可以
object HungrySingleton {
fun testXX() {
Log.e(TAG, "testXX: ")
}
}
2.懒汉式(线程安全)
优点:需要的时候才创建,可以传参
缺点:为了保证线程安全,getInstance方法是用synchronized修饰的,导致每次调用该方法的时候都会被同步,这样会消耗不必要的资源(不必要的同步开销)。所以这种模式一般不建议使用.
java:
public class LazySingleton {
private static LazySingleton instance;
private LazySingleton() {
}
public static synchronized LazySingleton getInstance() {
if (instance == null) {
instance = new LazySingleton();
}
return instance;
}
}
kotlin:
class LazySingleton {
//私有化构造方法
private constructor() {
}
companion object {
private var instance: LazySingleton? = null
//对外提供获取单例方法
@Synchronized
fun getDefault(): LazySingleton {
if (instance == null) {
instance = LazySingleton()
}
return instance!!
}
}
}
3.DCL(Double-Check-Lock双重校验锁)
对懒汉式进行了优化,只有在第一次实例化的时候才会走第二个分支,才会同步,避免了每次都同步造成的不必要的资源消耗。
优点:第一次执行getInstance方法时才会实例化,资源利用率高,效率高。
缺点:偶尔失效(由于JDK版本问题,在jdk1.5之前会失败)
java:
public class DCLSingleton {
//volatile关键字:
private volatile static DCLSingleton instance;
private DCLSingleton() {
}
public static DCLSingleton getInstance() {
if (instance == null) {
synchronized (DCLSingleton.class) {
if (instance == null) {
instance = new DCLSingleton();
}
}
}
return instance;
}
}
kotlin:
class DCLSingleton {
private constructor(){
}
companion object {
val instance: DCLSingleton by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
DCLSingleton()
}
}
}
4.静态内部类
java:
public class StaticInnerClass {
private static final String TAG = "StaticInnerClass";
public static String CLASS_INFO = "1班";
private StaticInnerClass() {
Log.e(TAG, "StaticInnerClass: ");
}
public static StaticInnerClass getInstance() {
return Creator.instance;
}
/**
* 静态内部类创建单例
* JVM机制:当一个类被加载的时候,类的加载过程是线程互斥的
* 所以其它语言不一定适用
*/
private static class Creator {
private static StaticInnerClass instance = new StaticInnerClass();
}
}
kotlin:
class Singleton{
private constructor(){
}
companion object {
val instance = SingletonHolder.holder
}
private object SingletonHolder {
val holder= Singleton()
}
}
当然还有其它很多实现单例模式的方法,比如利用枚举,容器等。还有一点需要注意的是反射和序列化可以破坏单例的设计目的。
总结:在Android中推荐适用DCL和静态内部类来实现单例。
ps:有次面试,面试官问我:最优的单例模式实现方法是什么?我当时不知道。面试官说是静态内部类。