Job 六大状态机原理
Job 是协程唯一的“句柄 (Handle)”。深刻理解 Job 的状态流转是掌握结构化并发的关键。
状态总览
| 状态 | isActive | isCompleted | isCancelled | 描述 |
|---|---|---|---|---|
| New | false | false | false | 刚创建,尚未启动 (懒加载模式下常见) |
| Active | true | false | false | 正在运行,或已挂起,或正在运行子协程 |
| Completing | true | false | false | 自身代码已跑完,正在等待子协程完成 |
| Cancelling | false | false | true | 正在取消中 (finally 块正在执行) |
| Cancelled | false | true | true | 已取消且 finally 执行完毕 |
| Completed | false | true | false | 正常执行完毕 |
状态流转图
mermaid
graph TD
New -- start() --> Active
Active -- 任务完成/等待子协程 --> Completing
Completing -- 所有子协程完成 --> Completed
Active -- cancel() --> Cancelling
Completing -- cancel() --> Cancelling
New -- cancel() --> Cancelled
Cancelling -- finally块结束 --> Cancelled隐秘状态:Completing
这是最容易让人困惑的状态。 当协程体内的最后一行代码执行完毕后,Job 不会立即变成 Completed,而是进入 Completing。 原因:结构化并发要求父协程必须等待所有子协程死掉(完成/取消)后,自己才能闭眼。
核心操作
等待 (join)
join() 是挂起函数。它会“卡住”调用它的协程,直到目标 Job 进入终态 (Completed 或 Cancelled)。
kotlin
val job = launch { delay(1000) }
job.join() // 挂起,直到 job 结束取消 (cancel)
cancel() 通常是普通的同步函数,它只是将 Job 状态置为 Cancelling 并向协程发送取消信号。 如果你想“取消并等待善后完成”,请用 cancelAndJoin()。
kotlin
val job = launch {
try {
delay(Long.MAX_VALUE)
} finally {
println("Cleaning up resource...")
delay(100) // 模拟清理耗时
println("Done cleanup")
}
}
// job.cancel() // 只发送取消信号,不等待清理完成
job.cancelAndJoin() // 等待 "Done cleanup" 打印后才继续往下走区分 Job 与 Deferred
Deferred<T> 是 Job 的子接口。
- Job: 只管生命周期 (Fire-and-forget)。
- Deferred: 携带结果 (Future)。
await(): 类似于join(),但会返回结果。如果 Job 被取消,await()会抛出异常。