Kotlin智能转换、空检查与Elvis运算符的线程安全性对比
问题解答
1. 类成员变量空检查后用!!返回会崩溃吗?
会。因为value是类的可变成员变量,多线程场景下会出现竞态条件:线程A执行完if (value != null)的空判断后,线程B可能立刻把value修改为null,等线程A执行return value!!时,就会触发空指针异常。
2. 两种实现的线程安全性对比
先贴出两个示例的代码:
Example 1
class Example { private var cachedVariable: SomeClass? = null fun getCachedVariable(): SomeClass { // 可变属性无法智能转换,所以用!!,但检查后被其他线程设为null会崩溃吗? if (cachedVariable != null) return cachedVariable!! cachedVariable = SomeClass() return cachedVariable!! } }
Example 2
class Example2 { private var cachedVariable: SomeClass? = null fun getCachedVariable(): SomeClass { // 这样能避免Example1的崩溃吗? return cachedVariable ?: SomeClass().also { cachedVariable = it } } }
安全性分析
- Example1仍有崩溃风险:和第一个问题的逻辑一致,
if (cachedVariable != null)通过后,其他线程可能将变量置为null,后续的return cachedVariable!!会触发空指针。 - Example2不会崩溃,但存在资源浪费问题:
?:运算符的逻辑是先读取cachedVariable的值,若为null则执行右侧逻辑。多线程同时进入时,会有多个线程创建SomeClass实例,最终cachedVariable会被最后一个线程的实例覆盖,前面创建的实例会被丢弃。但它不会触发空指针崩溃,因为返回的要么是已存在的非null实例,要么是刚创建的非null实例。
结论
仅从避免空指针崩溃的角度,Example2更优;但如果要实现真正线程安全的懒加载,这两种都不达标。推荐用Kotlin标准库的lazy委托(默认线程安全),或者手动加synchronized锁:
class SafeExample { private val cachedVariable by lazy { SomeClass() } fun getCachedVariable(): SomeClass = cachedVariable }
内容的提问来源于stack exchange,提问作者SpecialEd
相关产品推荐
相关产品推荐

