Singleton 单例模式

单列模式,是一种常用的软件设计模式,单列对象的类必须只有一个实例存在,并且自行实例化向整个系统提供

Java 构建方式

1. 懒汉模式

指全局的单列实例在第一次被使用时构建。

public class Singleton {
	private static Singleton INSTANCE = null;

	private Singleton() {}

	public static Singleton getInstance() {
		if (INSTANCE == null) {
			INSTANCE = new Singleton();
		}
		
		return INSTANCE;
	}
}

2. 饿汉模式

指全局的单列实例在类装载时构建。 线程不安全,当多个线程调用getInstance方法时,可能会创建多个实例。

public class Singleton {
	private final static Singleton INSTANCE = new Singleton();

	private Singleton() {}

	public static Singleton getInstance() {
		return INSTANCE;
	}
}

3. 双重锁模式

当多线程调用时,如果多线程同时执行完了第一次检查,其中一个inrush同步代码块创建了实例,后面的线程因第二次检测不会创建新实例。 使用volatile 禁止指令重排序优化。在volatile 变量的赋值操作后面会有一个内存屏障,读操作不会被重排序到内存屏障之前。

public class Singleton {
	private static volatile Singleton INSTANCE = null;

	private Singleton() {}

	public static Singleton getInstance() {
		if (INSTANCE == null) {
			synchronized(Singleton.class) {
				if (INSTANCE == null) {
					INSTANCE = new Singleton();
				}
			}
		}
		
		return INSTANCE;
	}
}

4. 实现Serializable接口

需要重写readResolve方法,才能保证其反序依旧是单列:

public class SingletonClass implement Serializable {
	private static SingletonClass INSTANCE = null;

	private SingletonClass() {}

	public static synchronized SingletonClass getInstance() {
		if (INSTANCE == null) {
			INSTANCE = new SingletonClass();
		}
		
		return INSTANCE;
	}

	// 如果实现了Serializable,必须重写这个方法
	private Object readResolve() throws ObjectStreamException {
		return INSTANCE;
	}
}

5. 枚举

public enum Singleton {
    INSTANCE;
}

INSTANCE;enum的一块语法糖,JVM会阻止反射获取枚举类的私有构造方法。使用枚举的方法起到了单例的作用,但也有弊端,就是无法进行懒加载。

优缺点

优点

  • 实例控制 单列模式会阻止其他对象实例化其自己的单列对象副本,从而确保所有对象都访问唯一实例。
  • 灵活性 因为类控制了实例化过程,所以类可以灵活更改实例化过程。

缺点

  • 开销 每次对象请求引用时都要检查是否存在类的实例
  • 可能的开发混淆
  • 对象生存期

Kotlin 构建方式

使用 Object 实现单例

object Singleton

使用 by lazy 实现单例

class Singleton private constructor() {

    companion object {
        // 方式二
        @JvmStatic
        val INSTANCE1 by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { WorkSingleton() }

        // 方式三 默认就是 LazyThreadSafetyMode.SYNCHRONIZED,可以省略不写,如下所示
        @JvmStatic
        val INSTANCE2 by lazy { WorkSingleton() }
    }
}

lazy 延迟模式的几种使用说明:

1. LazyThreadSafetyMode.SYNCHRONIZED

默认,如果有多个线程访问,只有一条线程可以去初始化 lazy 对象。

2. LazyThreadSafetyMode.PUBLICATION

对于还没有被初始化的 lazy 对象,可以被不同的线程调用,如果 lazy 对象初始化完成,其他的线程使用的是初始化完成的值。

3. LazyThreadSafetyMode.NONE

只能在单线程下使用,不能在多线程下使用,不会有锁的限制,也就是说它不会有任何线程安全的保证以及相关的开销。

可接收参数的单例

class Singleton private constructor(context: Context) {
  init {
    //Init using context argument
  }
  
  companion object : SingletionHolder<Singleton, Context>(::Singleton)
}

open class SingletonHolder<out T, in A>(creator: (A) -> T) {

    private var creator: ((A) -> T)? = creator

    @Volatile
    private var instance: T? = null

    fun getInstance(arg: A): T =
        instance ?: synchronized(this) {
            instance ?: creator!!(arg).apply {
                instance = this
            }
        }
}