启动模式详解 (CoroutineStart)
大部分开发者只使用默认行为,但特定场景下,选择正确的 CoroutineStart 模式能显著提升性能或保证逻辑安全。
| 模式 | 立即调度? | 立即挂起? | 可取消性 | 适用场景 |
|---|---|---|---|---|
DEFAULT | 是 | 否 | 随时可取消 | 常规业务 (90%) |
LAZY | 否 (需 start) | 否 | 随时可取消 | 依赖触发、懒加载 |
ATOMIC | 是 | 否 | 首个挂起点前不可取消 | 关键资源初始化 |
UNDISPATCHED | 否 (当前线程跑) | 否 | 随时可取消 | 极致性能优化 |
1. DEFAULT (默认)
调用 launch 后,协程立即进入调度器的队列。
- 注意:只是进入队列,并不代表立即执行。如果调度器忙,它需要等待。
2. LAZY (懒加载)
协程创建后呈 New 状态,不会执行,直到你调用 job.start() 或 join() / await()。
kotlin
val job = launch(start = CoroutineStart.LAZY) {
// 只有调用 start() 后才会打印
println("Work started")
}
// ... 做些准备工作 ...
job.start()3. ATOMIC (原子性)
这是一种“强硬”的启动模式。 特点:协程体内的代码直到遇到第一个挂起点(如 delay)之前,免疫取消。
kotlin
val job = launch(start = CoroutineStart.ATOMIC) {
// 即使在外面立即调用了 cancel(),这里也一定会在取消前执行
allocateCriticalResources()
delay(100) // ---> 只有在这里才会检查取消并抛出异常
}
job.cancel()4. UNDISPATCHED (不调度/立即执行)
这是性能黑客的利器。它让协程像普通函数一样,在当前线程立即同步执行,直到遇到第一个挂起点。
kotlin
launch(Dispatchers.Default, start = CoroutineStart.UNDISPATCHED) {
// 1. 立即在当前线程 (e.g., Main) 执行,不发生线程切换
println("Thread: ${Thread.currentThread().name}")
delay(100) // 2. 挂起
// 3. 恢复时,才会遵守 Dispatchers.Default,切换到后台线程
println("Thread: ${Thread.currentThread().name}")
}性能优势
它节省了第一次 context switching 和 dispatching 的开销。如果你有一堆微小的协程任务,且前一部分逻辑是非阻塞的,使用此模式可显著减少 CPU 消耗。