两者基本比较
把它们比作两种不同的"准备晚餐"方式:
1. by lazy 原理
想象成"点外卖"模式:
- 先下单(声明),但不立即配送(初始化)
- 第一次想吃的时候(首次访问)才开始配送(初始化)
- 之后再想吃就直接吃已送到的饭(缓存值)
class Restaurant {
// 相当于提前下单,但还没配送
private val dinner by lazy {
println("外卖开始配送...") // 初始化过程
"美味的晚餐" // 返回值
}
fun eat() {
println("准备吃晚餐: $dinner") // 首次访问才会配送
}
}
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
}
}
2. lateinit 原理
想象成"自己做饭"模式:
- 先准备厨具(声明变量)
- 等到需要的时候才去买菜做饭(延迟初始化)
- 如果还没做饭就想吃(访问未初始化变量)会出问题(抛出异常)
class Kitchen {
// 声明要做饭,但还没开始做
private lateinit var dinner: String
fun prepareDinner() {
dinner = "自制美味晚餐" // 初始化
}
fun eat() {
if (::dinner.isInitialized) { // 检查是否已经做好饭
println("开始吃晚餐: $dinner")
} else {
println("晚餐还没准备好!")
}
}
}
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);
}
}
3. 流程图对比
graph TD
A[声明 by lazy 属性] --> B{是否已初始化?}
B -->|是| C[返回缓存值]
B -->|否| D[执行初始化代码]
D --> E[缓存结果]
E --> C
F[声明 lateinit 变量] --> G{是否已初始化?}
G -->|是| H[返回变量值]
G -->|否| I[抛出异常]
4. 详细对比
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) // 现在可以安全使用
}
}
5. 使用场景对比
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 方法中初始化
}
6. 主要区别总结:
-
初始化时机
by lazy: 首次访问时自动初始化lateinit: 需要手动初始化
-
可空性
by lazy: 不可空,总是有值lateinit: 可能未初始化,访问时可能抛异常
-
属性类型
by lazy: 只能用于 val(不可变)lateinit: 只能用于 var(可变)
-
线程安全
by lazy: 默认线程安全lateinit: 不保证线程安全
-
内存占用
by lazy: 需要额外对象存储初始化逻辑lateinit: 较低的内存开销
7. 使用建议
class UsageRecommendations {
// 使用 by lazy 当:
private val config by lazy {
loadConfiguration() // 耗时操作
}
// 使用 lateinit 当:
private lateinit var binding: ActivityMainBinding
fun onCreate() {
binding = ActivityMainBinding.inflate(layoutInflater)
}
}
选择建议:
- 需要线程安全的延迟初始化 → 使用
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"
}
}
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
}
}
}
}
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()
}
}
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()
}
}
}
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"
}
}
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)
}
}
}
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")
}
}
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"
}
}
}
}
总结:
by lazy默认提供了线程安全保证,适合在多线程环境下使用lateinit没有内置的线程安全机制,需要时要手动添加同步- 在选择使用哪种方式时,需要考虑线程安全需求和性能要求