侧边栏壁纸
博主头像
colo

欲买桂花同载酒

  • 累计撰写 1823 篇文章
  • 累计收到 0 条评论

设计线程安全的单例模式,支持延迟初始化并避免Android内存泄漏

2025-12-12 / 0 评论 / 4 阅读

题目

设计线程安全的单例模式,支持延迟初始化并避免Android内存泄漏

信息

  • 类型:问答
  • 难度:⭐⭐⭐

考点

单例模式实现,线程安全,延迟初始化,内存泄漏预防,Kotlin语言特性

快速回答

实现要点:

  • 使用双重检查锁定(Double-Checked Locking)结合@Volatilesynchronized
  • 通过Application Context避免内存泄漏
  • 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依赖注入管理单例生命周期