一、先记核心结论
- 单个 launch / 单个 async 内部代码,一定是串行从上到下执行;
- 并发的本质不是一个协程内部乱序,而是「开多个独立协程」;
- 每写一个
launch{}/async{}就新建一条独立协程执行流 ,多个协程交给线程池调度,就形成并发。
| 方式 | 是否创建协程 | 内部执行 | 后面代码是否等待 |
|---|---|---|---|
| launch | ✅ 新建 | 串行 | 不等待(并发) |
| withContext | ❌ 不新建 | 串行 | 等待(串行) |
| async | ✅ 新建 | 串行 | 不等待(并发) |
二、通俗大白话
- 一个协程 = 一条单人流水线,里面任务只能排队串行;
- 你开 3 个 launch = 开 3 条流水线 ,三条流水线同时干活,就是并发。
三、代码演示:单个内部串行,多个之间并发
1. 单个 launch 内部:纯串行
kotlin
scss
lifecycleScope.launch(Dispatchers.IO) {
delay(1000)
println("任务1")
delay(1000)
println("任务2")
}
任务 1 执行完才会走 任务 2,内部严格串行。
2. 多个 launch:互相并发、互不等待
kotlin
scss
lifecycleScope.launch {
// 协程1
launch(Dispatchers.IO) {
delay(1000)
println("协程1 完成")
}
// 协程2
launch(Dispatchers.IO) {
delay(1000)
println("协程2 完成")
}
// 主线代码不等待,直接往下走
println("我先执行,不等两个子协程")
}
现象:两行日志几乎同时打印 ,不是先后等 2 秒,说明两个协程并发跑。
四、async 同理:单个串行,多个并发
kotlin
scss
scope.launch {
val d1 = async { delay(1000); println("A") }
val d2 = async { delay(1000); println("B") }
d1.await()
d2.await()
}
- 每个 async 内部串行;
- d1、d2 两个独立协程,并发同时延时,总共只耗时 1 秒,不是 2 秒。
五、面试标准口述答案(直接背)
launch 和 async 各自内部代码都是串行顺序执行 ,因为一个协程本身就是单执行流,只能从上到下依次跑。但 launch、async 都是协程构建器,每调用一次就会创建一个全新独立的子协程 。当我们同时开启多个 launch 或 async 时,多个独立协程被调度器分发到线程池不同线程或复用线程调度执行,多个协程同时运行 ,从而实现了并发。所以并发靠的是多协程并行调度,不是单个协程内部乱序执行。
六、关键总结一句话
单个协程内部永远串行;并发 = 开启多个独立协程一起跑。