Job 状态机深度解析
Job 的核心职责是管理协程的生命周期。它内部维护了一个复杂的状态机,确保父子关系和取消逻辑的正确执行。
六大状态与流转
一个 Job 包含以下六种逻辑状态,但在 API 层面,我们只能通过 isActive、isCompleted 和 isCancelled 三个属性来观测。
| 状态 | isActive | isCompleted | isCancelled | 描述 |
|---|---|---|---|---|
New | false | false | false | 已创建,但未启动(LAZY 模式) |
Active | true | false | false | 运行中或等待子项 |
Completing | true | false | false | 自身完成,正在等待子项完成 |
Cancelling | false | false | true | 正在被取消,等待清理或子项取消 |
Cancelled | false | true | true | 已被取消并清理完毕 |
Completed | false | true | false | 正常执行完毕 |
状态流转图
mermaid
graph TD
New -- start --> Active
Active -- finish --> Completing
Active -- cancel --> Cancelling
Completing -- all children finished --> Completed
Cancelling -- all children finished --> Cancelled
Active -- failure --> Cancelling关键中间态:Completing
为什么需要 Completing 状态?
在结构化并发中,父协程的代码即便运行完了,它也必须等待所有子协程完成。此时父协程就处于 Completing。它依然是 isActive 的,但无法再启动新的子协程。
内部实现:状态字段
在 JobSupport 源码中,状态被存储在一个单一的 state 变量中(通常是 AtomicReference)。
Empty: 简单的初始态。NodeList: 当有子项或监听器注册时,状态会升级为一个包含双向链表的对象。Finishing: 对应Completing和Cancelling,内部持有一个列表来跟踪所有活跃的子项。
监听机制:invokeOnCompletion
你可以通过 invokeOnCompletion 在 Job 进入终态时执行回调。
kotlin
val job = launch { /* ... */ }
job.invokeOnCompletion { cause ->
if (cause == null) {
println("Job finished normally")
} else {
println("Job failed or cancelled with $cause")
}
}内存注意
invokeOnCompletion 返回一个 DisposableHandle。如果协程生命周期非常长,而你动态注册了大量监听器,记得手动 dispose,否则会造成内存压力。
核心准则总结
- 不要在
Cancelled状态下尝试恢复业务:此时 Job 已不可用。 - 理解
isCompleted的含义:它包含正常完成和取消。 - 善用
Completing逻辑:这是确保父级能为子级“收尸”的底层保证。