Kotlin 协程底层原理(Continuation)详解
Kotlin 协程底层原理Continuation详解很多人会用协程launch{}async{}withContext{}但真正理解协程底层后你会发现Kotlin 协程本质上并不是魔法异步。它本质是Continuation续体 状态机这才是协程真正核心。一、先理解为什么需要 Continuation先看普通函数。普通函数执行过程funtest(){println(1)println(2)println(3)}执行流程1 - 2 - 3函数执行完栈帧销毁无法恢复这叫一次性执行。但协程不同。二、协程为什么能暂停后恢复suspendfunload(){println(开始)delay(3000)println(结束)}问题来了delay后协程暂停了。3秒后它为什么还能继续从println(结束)开始执行普通函数做不到。所以Kotlin 编译器做了一件事把 suspend 函数改造成**“状态机”**而Continuation就是保存状态的核心对象。三、Continuation 是什么官方定义协程执行到哪了的记录器。或者更简单Continuation 协程存档点它会记录当前执行位置局部变量下一步执行逻辑协程上下文四、最简单理解你玩游戏打到一半存档退出游戏下次继续Continuation就是这个存档。五、suspend 本质是什么先看suspendfunrequest():String很多人以为suspend是关键字魔法。其实编译后suspend函数会变成funrequest(continuation:ContinuationString):Any重点来了所有 suspend 函数都会多一个 Continuation 参数这是协程底层最大核心。六、编译器偷偷干了什么你写suspendfuntest(){delay(1000)println(完成)}编译器会生成类似classTestContinuation:ContinuationUnit{varlabel0overridefunresumeWith(result:ResultUnit){when(label){0-{label1delay(1000,this)}1-{println(完成)}}}}这就是状态机State Machine七、label 是什么varlabel0它表示当前协程执行到了哪个阶段示例suspendfundemo(){println(A)delay(1000)println(B)delay(1000)println(C)}编译后类似label位置0开始1第一个delay后2第二个delay后所以协程恢复时只需要看 label就知道该从哪里继续。八、delay 为什么不会阻塞线程这是经典面试题。Thread.sleepThread.sleep(3000)线程彻底卡死delaydelay(3000)发生了什么第一步协程保存现场Continuation 存档包括label、局部变量、执行位置第二步当前线程被释放线程去执行别的任务第三步时间到了恢复 Continuationcontinuation.resume()协程继续执行所以delay 挂起的是协程不是线程。九、resume 是什么Continuation 最核心方法resumeWith()作用恢复协程执行举例协程暂停在delay时间到 →resume()协程继续十、真正的协程恢复流程代码launch{delay(1000)println(Hello)}底层流程1. launch 创建协程 → 生成Continuation对象 2. 执行到 delay → 发现需要挂起 3. 保存状态 → 保存label、局部变量、上下文 4. 当前线程释放 → 线程不阻塞 5. 1秒后 → 定时器触发continuation.resume() 6. 状态机恢复 → 从label 1 继续执行十一、协程为什么这么轻量线程协程OS内核调度用户态对象成本高只是一堆 Continuation 状态机对象所以一个线程可以跑几十万个协程十二、挂起点Suspension Point只有suspend函数才能挂起。例如delay()withContext()await()这些都叫挂起点十三、为什么 suspend 只能在协程里调用因为suspend函数需要Continuation普通函数没有。所以funmain(){delay(1000)// 会报错}十四、withContext 为什么能切线程withContext(Dispatchers.IO)本质修改 Continuation 的 Dispatcher恢复协程时调度器决定在哪个线程继续执行十五、CoroutineContext协程上下文内部保存DispatcherJobCoroutineNameExceptionHandler本质就是Map结构十六、Job 底层原理每个协程都有 JobJob 维护父子协程关系cancel()本质修改协程状态然后挂起点检测取消十七、为什么协程取消不是立刻停止因为协程是协作式取消不是线程强杀。例如while(true){}// 不会停因为没有检查取消状态正确while(isActive){}十八、suspend 不等于异步这是最容易误解的。错误理解正确理解suspend 开新线程suspend只是支持挂起与恢复是否异步看你是否launch/async十九、真正的协程核心结构协程底层可以简化成Coroutine ↓ Continuation ↓ StateMachine ↓ Dispatcher二十、协程 vs 回调回调地狱request{request2{request3{// ...}}}协程valarequest()valbrequest2()valcrequest3()为什么能这样因为 Continuation 替你保存了执行现场。二十一、Retrofit 为什么支持 suspendRetrofit 遇到suspend fun getUser()本质它拿到了Continuation网络返回后调用continuation.resume(data)协程恢复。二十二、最重要的一张图普通函数执行 - 结束 - 消失协程执行 ↓ 挂起 ↓ 保存Continuation ↓ 线程释放 ↓ 恢复resume ↓ 继续执行二十三、协程源码里最重要的类真正核心Continuation基类ContinuationImplBaseContinuationImplDispatchedContinuation二十四、源码中最经典的方法resumeWith()协程恢复入口。二十五、面试高频题非常重要1. suspend 本质是什么本质编译器 CPS 转换即Continuation Passing Style编译器自动添加Continuation参数。2. 协程为什么不卡线程因为挂起的是协程不是线程3. 协程为什么轻量因为不走系统线程调度本质只是对象和状态机4. delay 为什么能恢复因为continuation.resume()5. 协程如何记住执行位置通过label 状态机二十六、真正理解协程的一句话Kotlin 协程本质编译器把 suspend 函数转换成状态机然后Continuation 保存协程执行现场最终实现可暂停、可恢复、非阻塞