两者基本比较
把它们比作两种不同的"准备晚餐"方式:
1. by lazy 原理
想象成"点外卖"模式:
- 先下单(声明),但不立即配送(初始化)
- 第一次想吃的时候(首次访问)才开始配送(初始化)
- 之后再想吃就直接吃已送到的饭(缓存值)
by lazy 的底层实现:
// 简化的底层实现原理
class LazyImpl<T> {
private var value: T? = null
private var initialized = false
operator fun getValue(thisRef: Any?, property: KProperty<*>): T {
if (!initialized) {
synchronized(this) {
if (!initialized) {
value = initializer() // 调用初始化lambda
initialized = true
}
}
}
return value as T
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
2. lateinit 原理
想象成"自己做饭"模式:
- 先准备厨具(声明变量)
- 等到需要的时候才去买菜做饭(延迟初始化)
- 如果还没做饭就想吃(访问未初始化变量)会出问题(抛出异常)
lateinit 的底层实现:
// 反编译后的Java代码简化版
public class Kitchen {
private String dinner; // 不会有默认值
public final void prepareDinner() {
this.dinner = "自制美味晚餐";
}
public final void eat() {
if (this.dinner == null) {
throw new UninitializedPropertyAccessException("dinner");
}
System.out.println("开始吃晚餐: " + this.dinner);
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
3. 详细对比
class ComparisonExample {
// by lazy 示例
private val lazyValue by lazy {
println("初始化 lazy 值")
"Lazy Value"
}
// lateinit 示例
private lateinit var lateinitValue: String
fun demo() {
// lazy: 首次访问时初始化
println(lazyValue) // 打印初始化消息和值
println(lazyValue) // 直接使用缓存值
// lateinit: 需要手动初始化
try {
println(lateinitValue) // 如果未初始化会抛出异常
} catch (e: UninitializedPropertyAccessException) {
println("lateinit 变量未初始化")
}
lateinitValue = "Lateinit Value" // 初始化
println(lateinitValue) // 现在可以安全使用
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
4. 使用场景对比
class UsageScenarios {
// by lazy 适合:
private val heavyResource by lazy {
// 1. 计算成本高的初始化
// 2. 可能不会使用的资源
// 3. 需要确保线程安全的场景
loadHeavyResource()
}
// lateinit 适合:
private lateinit var dependency: SomeService
// 1. 依赖注入
// 2. Android Activity/Fragment 的视图绑定
// 3. 单元测试的 setup 方法中初始化
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
5. 主要区别总结:
- 初始化时机
by lazy
: 首次访问时自动初始化lateinit
: 需要手动初始化
- 可空性
by lazy
: 不可空,总是有值lateinit
: 可能未初始化,访问时可能抛异常
- 属性类型
by lazy
: 只能用于 val(不可变)lateinit
: 只能用于 var(可变)
- 线程安全
by lazy
: 默认线程安全lateinit
: 不保证线程安全
- 内存占用
by lazy
: 需要额外对象存储初始化逻辑lateinit
: 较低的内存开销
6. 使用建议
选择建议:
- 需要线程安全的延迟初始化 → 使用
by lazy
- 依赖注入或框架初始化 → 使用
lateinit
- 需要可变属性 → 使用
lateinit
- 需要确保只初始化一次 → 使用
by lazy
两者线程安全问题
1. by lazy 的线程安全机制
by lazy
默认使用 SYNCHRONIZED
模式,内部使用了同步锁来保证线程安全:
class ThreadSafetyDemo {
// 默认是 SYNCHRONIZED 模式
private val lazyValue by lazy {
println("初始化 lazy 值,线程:${Thread.currentThread().name}")
"Lazy Value"
}
// 等同于以下显式声明
private val explicitLazyValue by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
println("初始化 lazy 值,线程:${Thread.currentThread().name}")
"Lazy Value"
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
by lazy 的底层实现:
// 简化的 SynchronizedLazyImpl 实现
private class SynchronizedLazyImpl<T>(initializer: () -> T) : Lazy<T> {
private var value: Any? = UNINITIALIZED_VALUE
private var initializer: (() -> T)? = initializer
@Volatile private var _value: Any? = UNINITIALIZED_VALUE
override val value: T
get() {
val _v1 = _value
if (_v1 !== UNINITIALIZED_VALUE) {
@Suppress("UNCHECKED_CAST")
return _v1 as T
}
return synchronized(this) {
val _v2 = _value
if (_v2 !== UNINITIALIZED_VALUE) {
@Suppress("UNCHECKED_CAST")
_v2 as T
} else {
val typedValue = initializer!!()
_value = typedValue
initializer = null
typedValue
}
}
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
2. lateinit 的非线程安全性
lateinit
没有任何同步机制,在多线程环境下可能出现问题:
class LateinitThreadIssueDemo {
private lateinit var sharedResource: MutableList<String>
// 可能出现线程安全问题的代码
fun initializeInMultiThread() {
Thread {
if (!::sharedResource.isInitialized) {
sharedResource = mutableListOf()
}
sharedResource.add("Thread 1")
}.start()
Thread {
if (!::sharedResource.isInitialized) {
sharedResource = mutableListOf()
}
sharedResource.add("Thread 2")
}.start()
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
3. 线程安全问题演示
class ThreadSafetyExample {
// by lazy 线程安全演示
private val safeLazyList by lazy {
println("初始化 lazy list")
mutableListOf<String>()
}
// lateinit 非线程安全演示
private lateinit var unsafeLateinitList: MutableList<String>
fun testThreadSafety() {
// 创建多个线程同时访问
repeat(10) { threadId ->
Thread {
// by lazy 是线程安全的
safeLazyList.add("Item from thread $threadId")
// lateinit 可能出现问题
if (!::unsafeLateinitList.isInitialized) {
unsafeLateinitList = mutableListOf()
}
unsafeLateinitList.add("Item from thread $threadId")
}.start()
}
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
4. 线程安全的不同模式
by lazy 提供了三种线程安全模式:
class LazyThreadSafetyModes {
// 1. SYNCHRONIZED: 默认模式,线程安全
private val synchronizedValue by lazy(LazyThreadSafetyMode.SYNCHRONIZED) {
println("线程安全初始化")
"Synchronized Value"
}
// 2. PUBLICATION: 可能多次执行初始化,但只会使用第一个完成的结果
private val publicationValue by lazy(LazyThreadSafetyMode.PUBLICATION) {
println("可能执行多次,但只用第一个结果")
"Publication Value"
}
// 3. NONE: 不提供任何线程安全保证
private val unsafeValue by lazy(LazyThreadSafetyMode.NONE) {
println("非线程安全初始化")
"Unsafe Value"
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
5. 如何使 lateinit 线程安全
如果需要让 lateinit 变量线程安全,需要手动添加同步机制:
class ThreadSafeLateinit {
private lateinit var resource: MutableList<String>
private val initLock = Any()
fun safeInitialize() {
synchronized(initLock) {
if (!::resource.isInitialized) {
resource = mutableListOf()
}
}
}
fun safeAdd(item: String) {
synchronized(initLock) {
if (!::resource.isInitialized) {
resource = mutableListOf()
}
resource.add(item)
}
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
6. 性能对比
class PerformanceComparison {
// by lazy: 有额外的同步开销
private val lazyValue by lazy {
heavyComputation()
}
// lateinit: 没有同步开销,但需要手动处理线程安全
private lateinit var lateinitValue: String
fun measurePerformance() {
val startTime1 = System.nanoTime()
println(lazyValue)
val lazyTime = System.nanoTime() - startTime1
val startTime2 = System.nanoTime()
lateinitValue = heavyComputation()
println(lateinitValue)
val lateinitTime = System.nanoTime() - startTime2
println("Lazy time: $lazyTime")
println("Lateinit time: $lateinitTime")
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
7. 使用建议
class UsageRecommendations {
// 1. 单线程环境:可以使用 lateinit
private lateinit var singleThreadResource: String
// 2. 多线程环境,需要线程安全:使用 by lazy
private val threadSafeResource by lazy {
"Thread safe initialization"
}
// 3. 多线程环境,但性能关键:使用 lateinit + 自定义同步
private lateinit var performanceCriticalResource: String
private val lock = Any()
fun initializeWithCustomSync() {
synchronized(lock) {
if (!::performanceCriticalResource.isInitialized) {
performanceCriticalResource = "Custom synchronized initialization"
}
}
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
总结:
by lazy
默认提供了线程安全保证,适合在多线程环境下使用lateinit
没有内置的线程安全机制,需要时要手动添加同步- 在选择使用哪种方式时,需要考虑线程安全需求和性能要求