单例模式
引言
本文主要探讨单例模式的几种实现方式及优缺点
- 饿汉式
- 静态内部类式
- 懒汉式
- 线程安全的懒汉式
- 双重校验锁式
详细介绍
饿汉式
- 基本不会使用,程序启动或类被加载时,对象就会实例化
- 浪费资源
object InstanceDemo
// 查看字节码:Tools -> Kotlin -> Show Kotlin Bytecode
// class version 52.0 (52)
// access flags 0x31
public final class com/dcxing/InstanceDemo {
// access flags 0x2
private <init>()V
L0
LINENUMBER 3 L0
ALOAD 0
INVOKESPECIAL java/lang/Object.<init> ()V
RETURN
L1
LOCALVARIABLE this Lcom/dcxing/InstanceDemo; L0 L1 0
MAXSTACK = 1
MAXLOCALS = 1
// access flags 0x19
public final static Lcom/dcxing/InstanceDemo; INSTANCE
@Lorg/jetbrains/annotations/NotNull;() // invisible
// access flags 0x8
static <clinit>()V
L0
LINENUMBER 3 L0
NEW com/dcxing/InstanceDemo
DUP
INVOKESPECIAL com/dcxing/InstanceDemo.<init> ()V
ASTORE 0
ALOAD 0
PUTSTATIC com/dcxing/InstanceDemo.INSTANCE : Lcom/dcxing/InstanceDemo;
RETURN
MAXSTACK = 2
MAXLOCALS = 1
@Lkotlin/Metadata;(mv={1, 6, 0}, k=1, d1={"\u0000\u000c\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\u0008\u0002\u0008\u00c6\u0002\u0018\u00002\u00020\u0001B\u0007\u0008\u0002\u00a2\u0006\u0002\u0010\u0002\u00a8\u0006\u0003"}, d2={"Lcom/dcxing/InstanceDemo;", "", "()V", "android_testRelease"})
// compiled from: InstanceDemo.kt
}
// 通过Decompile,可以看到和java声明方式一样
@Metadata(
mv = {1, 6, 0},
k = 1,
d1 = {"\u0000\f\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\bÆ\u0002\u0018\u00002\u00020\u0001B\u0007\b\u0002¢\u0006\u0002\u0010\u0002¨\u0006\u0003"},
d2 = {"Lcom/dcxing/InstanceDemo;", "", "()V", "android_testRelease"}
)
public final class InstanceDemo {
@NotNull
public static final InstanceDemo INSTANCE;
private InstanceDemo() {
}
static {
InstanceDemo var0 = new InstanceDemo();
INSTANCE = var0;
}
}
静态内部类式
- 既可以做到延迟加载,又不必使用同步关键字
- 线程安全(java中class加载互斥)
- 减少内存消耗
- 单例中存在多线程环境,内部类可避免该问题
- jvm对类初始化做了限制,同一时间只允许一个线程去初始化一个类,从虚拟机层面避免大部分单例实现问题
class InstanceDemo private constructor() {
companion object {
val instance = InstanceHolder.holder
}
private object InstanceHolder {
val holder = InstanceDemo()
}
}
懒汉式
- 延迟加载,按需加载(第一次访问时创建)
- 线程不安全,多线程易出现不同步情况
class InstanceDemo private constructor() {
companion object {
private var instance: InstanceDemo? = null
get() {
if (field == null) {
field = InstanceDemo()
}
return field
}
fun get(): InstanceDemo {
// 此处不用getInstance作为方法名是因为伴生对象声明时,内部已有getInstance方法,所以要用其他名字
return instance!!
}
}
}
线程安全的懒汉式
- 再懒汉式基础上增加同步锁:@Synchronized
- 解决线程不安全问题
- 效率低,每次访问都要判断同步锁
- Android中InputManager、AccessibilityManager等使用此方式实例化
class InstanceDemo private constructor() {
companion object {
private var instance: InstanceDemo? = null
get() {
if (field == null) {
field = InstanceDemo()
}
return fieled
}
@Synchronized
fun get(): InstanceDemo {
// 此处不用getInstance作为方法名是因为伴生对象声明时,内部已有getInstance方法,所以要用其他名字
return instance!!
}
}
}
双重校验锁式
- 并发量不多,安全性不高的情况下可正常运行,但在jvm编译过程中会出现指令重排优化过程,会导致其实际未被初始化就分配内存空间,导致其不完整(www.360doc.com/content/11/…)
- 大部分项目使用此方式创建
// 无参
class InstanceDemo private constructor() {
companion object {
val instance: InstanceDemo by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { InstanceDemo() }
}
}
// 有参
class InstanceDemo private constructor(private val context: Context) {
companion object {
@SuppressLint("StaticFieldLeak")
@Volatile private var instance: InstanceDemo? = null
fun getInstance(context: Context) =
instance ?: synchronized(this) {
instance ?:InstanceDemo(context).also { instance = it }
}
}
}