两者基本比较

把它们比作两种不同的"准备晚餐"方式:

1. by lazy 原理

想象成"点外卖"模式:

  • 先下单(声明),但不立即配送(初始化)
  • 第一次想吃的时候(首次访问)才开始配送(初始化)
  • 之后再想吃就直接吃已送到的饭(缓存值)
class Restaurant {
    // 相当于提前下单,但还没配送
    private val dinner by lazy {
        println("外卖开始配送...")  // 初始化过程
        "美味的晚餐"  // 返回值
    }
    
    fun eat() {
        println("准备吃晚餐: $dinner")  // 首次访问才会配送
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
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 原理

想象成"自己做饭"模式:

  • 先准备厨具(声明变量)
  • 等到需要的时候才去买菜做饭(延迟初始化)
  • 如果还没做饭就想吃(访问未初始化变量)会出问题(抛出异常)
class Kitchen {
    // 声明要做饭,但还没开始做
    private lateinit var dinner: String
    
    fun prepareDinner() {
        dinner = "自制美味晚餐"  // 初始化
    }
    
    fun eat() {
        if (::dinner.isInitialized) {  // 检查是否已经做好饭
            println("开始吃晚餐: $dinner")
        } else {
            println("晚餐还没准备好!")
        }
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
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. 主要区别总结:

  1. 初始化时机
  • by lazy: 首次访问时自动初始化
  • lateinit: 需要手动初始化
  1. 可空性
  • by lazy: 不可空,总是有值
  • lateinit: 可能未初始化,访问时可能抛异常
  1. 属性类型
  • by lazy: 只能用于 val(不可变)
  • lateinit: 只能用于 var(可变)
  1. 线程安全
  • by lazy: 默认线程安全
  • lateinit: 不保证线程安全
  1. 内存占用
  • by lazy: 需要额外对象存储初始化逻辑
  • lateinit: 较低的内存开销

6. 使用建议

class UsageRecommendations {
    // 使用 by lazy 当:
    private val config by lazy {
        loadConfiguration()  // 耗时操作
    }
    
    // 使用 lateinit 当:
    private lateinit var binding: ActivityMainBinding
    
    fun onCreate() {
        binding = ActivityMainBinding.inflate(layoutInflater)
    }
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.

选择建议:

  1. 需要线程安全的延迟初始化 → 使用 by lazy
  2. 依赖注入或框架初始化 → 使用 lateinit
  3. 需要可变属性 → 使用 lateinit
  4. 需要确保只初始化一次 → 使用 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.

总结:

  1. by lazy 默认提供了线程安全保证,适合在多线程环境下使用
  2. lateinit 没有内置的线程安全机制,需要时要手动添加同步
  3. 在选择使用哪种方式时,需要考虑线程安全需求和性能要求