题目
设计线程安全的单例模式,支持延迟初始化并避免Android内存泄漏
信息
- 类型:问答
- 难度:⭐⭐⭐
考点
单例模式实现,线程安全,延迟初始化,内存泄漏预防,Kotlin语言特性
快速回答
实现要点:
- 使用双重检查锁定(Double-Checked Locking)结合
@Volatile和synchronized - 通过
ApplicationContext避免内存泄漏 - Kotlin特性:
by lazy委托的线程安全模式选择 - 防止反射/序列化破坏单例
原理说明
在Android中实现线程安全的延迟初始化单例需解决:1) 多线程环境下的初始化竞争 2) 避免持有Activity Context导致内存泄漏 3) 防止反射/序列化破坏单例结构。Kotlin的object是饿汉式单例,不满足延迟初始化需求。
代码示例
class SafeSingleton private constructor(context: Context) {
// 持有Application Context
val appContext = context.applicationContext
companion object {
@Volatile private var instance: SafeSingleton? = null
fun getInstance(context: Context): SafeSingleton {
return instance ?: synchronized(this) {
instance ?: SafeSingleton(context).also {
instance = it
}
}
}
}
// 防止反射破坏
private fun readResolve(): Any = instance!!
}
// 使用by lazy的替代方案
class LazySingleton private constructor(context: Context) {
companion object {
private var instance: LazySingleton? = null
fun getInstance(context: Context): LazySingleton =
instance ?: synchronized(this) {
instance ?: LazySingleton(context.applicationContext).also { instance = it }
}
}
}
// 使用LazyThreadSafetyMode.SYNCHRONIZED
val safeSingleton by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
SingletonHolder(context)
}最佳实践
- Context处理:始终使用
context.applicationContext,避免持有Activity引用 - 线程安全:双重检查锁定中
@Volatile确保可见性,synchronized保证原子性 - Kotlin优化:
by lazy选择LazyThreadSafetyMode.SYNCHRONIZED模式 - 防破坏:添加
private fun readResolve()防止序列化破坏单例
常见错误
- 错误1:直接使用
object声明(饿汉式初始化)object LeakySingleton { lateinit var context: Context // 可能持有Activity引用 } - 错误2:缺少
@Volatile导致指令重排序// 错误示例:非原子操作可能导致多次初始化 if (instance == null) { // 线程A通过检查 synchronized(this) { instance = SafeSingleton() // 线程B可能已修改 } } - 错误3:使用
by lazy默认模式初始化耗时操作阻塞UI线程
扩展知识
- DCL原理:
@Volatile禁止JVM指令重排序,保证instance写入发生在构造函数完全执行后 - 内存泄漏检测:使用LeakCanary监控单例生命周期
- Kotlin委托:
by lazy的三种模式:SYNCHRONIZED:线程安全(默认)PUBLICATION:允许多线程初始化但只返回第一个结果NONE:非线程安全
- 现代替代方案:使用Hilt依赖注入管理单例生命周期