Skip to content

Job 六大状态机原理

源:Job 源码逻辑解析

Job 是协程唯一的“句柄 (Handle)”。深刻理解 Job 的状态流转是掌握结构化并发的关键。

状态总览

状态isActiveisCompletedisCancelled描述
Newfalsefalsefalse刚创建,尚未启动 (懒加载模式下常见)
Activetruefalsefalse正在运行,或已挂起,或正在运行子协程
Completingtruefalsefalse自身代码已跑完,正在等待子协程完成
Cancellingfalsefalsetrue正在取消中 (finally 块正在执行)
Cancelledfalsetruetrue已取消且 finally 执行完毕
Completedfalsetruefalse正常执行完毕

状态流转图

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 进入终态 (CompletedCancelled)。

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() 会抛出异常。