单例模式的意义:
确保一个类只有一个实例,而且自行实例化并且向系统提供这个实例。
单例模式的实现条件
①一个private的构造函数,使得这个类只能在内部实例化。
②通过静态方法或者枚举获得单例类对象
③确保单例类有且只有一个,特别是多线程的情况下
④确保单例类对象在反序列化时不会重新构建对象
实现方式
1、饿汉模式
// Java 实现
public class Singleton {
private static Singleton instance = new Singleton();
/**
* 构造私有函数
*/
private Singleton() {
}
/**
* 公有的静态函数,对外暴露获取单例对象的接口
*/
public static Singleton getInstance() {
return instance;
}
}
//Kotlin实现
object Singleton {
}
其实kotlin已经没有了静态说法了,但kotlin的对象声明,功能上的确是满足了单例模式的实现要求,对象声明的初始化过程是线程安全的。另外,如果你想用kotlin的伴生对象,也是可以的。
饿汉模式很简单,通过私有构造方法和Java的ClassLoader机制来保证单例对象的唯一。
2、懒汉模式
懒汉模式其实就是上面饿汉模式的优化版本,把类的实例化时机延后,在第一次使用的时候才实例化。
public class Singleton {
private static Singleton instance;
/**
* 构造私有函数
*/
private Singleton() {
}
/**
* 公有的静态函数,对外暴露获取单例对象的接口
*/
public static synchronized Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
//Kotlin实现
class SingletonDemo private constructor() {
companion object {
private var instance: SingletonDemo? = null
get() {
if (field == null) {
field = SingletonDemo()
}
return field
}
@Synchronized
fun get(): SingletonDemo{
//instance声明是,会自动创建一个getInstance名字的方法,所以只能换个名字
return instance!!
}
}
}
实例化的代码在可外部调用的getInstance方法里面了,所以要确保单例的唯一性,只能在方法上加上synchronized来进行同步。
缺点也很明显,就是每次调用getInstance方法,都要进行同步,耗费资源。
3、双重校验锁
public class Singleton {
/**
* 构造私有函数
*/
private Singleton() {
}
//第一层锁:保证变量可见性
private volatile static Singleton instance = null;
/**
* 公有的静态函数,对外暴露获取单例对象的接口
*/
public static Singleton getInstance() {
if (instance == null) {//第一次判空:无需每次都加锁,提高性能
synchronized (Singleton.class){//第二层锁:保证线程同步
if (instance == null) {//第二次判空:避免多线程同时执行getInstance()产生多个single对象
instance = new Singleton();
}
}
}
return instance;
}
}
//kotlin实现
class SingletonDemo private constructor() {
companion object {
val instance: SingletonDemo by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
SingletonDemo() }
}
}
这里就是用了伴生对象companion object,使得可以用类名的方式来调用instance;
限定构造方法为private,保持实例的唯一性;
使用lazy{}高价函数来实例化,实现了延迟加载,并且lazy函数是线程安全的;
延迟属性lazy语法和解释
4、静态内部类
public class Singleton {
/**
* 构造私有函数
*/
private Singleton() {
}
/**
* 公有的静态函数,对外暴露获取单例对象的接口
*/
public static Singleton getInstance() {
return Holder.INSTANCE;
}
private static class Holder {
private static final Singleton INSTANCE = new Singleton();
}
}
class Singleton private constructor() {
companion object {
val instance = Holder.holder
}
private object Holder {
val holder = Singleton()
}
}
使用静态内部类的方式,是因为双重校验锁有时候也会失效。
看完所有实现方法,kotlin相比Java实在是简单了很多,kotlin语法自身帮我们完成了很多事情。