You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何Code A的双重检查锁实现无法返回Singleton实例?

为什么两种双重检查锁定实现的单例在Compose中表现不同?

我原本认为Code A与Code B的实现等价,通过调用preferenceParState: PreferenceParameterState = rememberPreferenceParameterState()都能返回单例实例。但通过preferenceParState.hashCode()测试发现,Code B可以返回单例,Code A却不行,另外测试发现Code A中不同实例的mContext.hashCode()值是相同的。

Code A

class PreferenceParameterState private constructor(context: Context) {

    companion object {     

       @Volatile private var instance: PreferenceParameterState? = null

        fun getInsance(context: Context): PreferenceParameterState{          
            return instance ?: synchronized(this) {
                instance ?: PreferenceParameterState(context)
            }
        }
    }
}


@Composable
fun rememberPreferenceParameterState(): PreferenceParameterState {
    val mContext= LocalContext.current
    return remember {
        PreferenceParameterState.getInsance(mContext)
    }
}

Code B

class PreferenceParameterState private constructor(context: Context) {
    companion object {
  
         @Volatile private var instance: PreferenceParameterState? = null
        fun getInsance(context: Context): PreferenceParameterState{
            if (instance == null) {
                synchronized(this) {
                    if (instance == null) {
                        instance = PreferenceParameterState(context)
                    }
                }
            }
            return instance!!
        }
    }
}


@Composable
fun rememberPreferenceParameterState(): PreferenceParameterState {
    val mContext= LocalContext.current
    return remember {
        PreferenceParameterState.getInsance(mContext)
    }
}

问题原因分析

核心差异在于双重检查锁定中是否对单例变量完成赋值:

  • Code A的致命漏洞:同步块内的instance ?: PreferenceParameterState(context)只是创建了新实例,但并没有将这个实例赋值给companion object中的instance变量。也就是说,每次调用getInsance时,只要外层的instance还是null,就会进入同步块创建新实例,但instance变量始终保持null,导致后续调用会重复创建新对象,自然无法实现单例。
  • Code B的正确实现:同步块内明确执行了instance = PreferenceParameterState(context),将新建的实例引用赋值给instance变量。后续调用getInsance时,外层检查会发现instance不为null,直接返回已有实例,实现了单例效果。

至于Code A中不同实例的mContext.hashCode()相同,是因为LocalContext.current在同一Compose环境下返回的是同一个Context实例(通常是Application Context或当前Activity的Context),所以即使PreferenceParameterState被多次创建,它们持有的Context引用是同一个,因此hashCode一致。

内容的提问来源于stack exchange,提问作者HelloCW

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 17:37:25