当前位置: 首页 > news >正文

Kotlin协程的JVM实现源码分析(上)

本文从协程的启动launch源码入手分析,协程JVM实现分为两篇:

  1. 协程启动和执行源码分析
  2. 无栈协程 和 Continuation

基本环境:

  • IntelliJ IDEA 2023.3.2
  • Kotlin 1.8.20
  • kotlinx-coroutines-core 1.7.3
  • gradle 8.2

一、协程的启动和执行

GlobalScope.launch 启动协程分析:

public fun CoroutineScope.launch(context: CoroutineContext = EmptyCoroutineContext,start: CoroutineStart = CoroutineStart.DEFAULT,block: suspend CoroutineScope.() -> Unit
): Job {val newContext = newCoroutineContext(context)val coroutine = if (start.isLazy)LazyStandaloneCoroutine(newContext, block) elseStandaloneCoroutine(newContext, active = true)coroutine.start(start, coroutine, block)return coroutine
}

调用关系:CoroutineScope.launch -> StandaloneCoroutine.start ->
CoroutineStart.invoke -> block.startCoroutineCancellable

1. startCoroutineCancellable 启动流程

启动协程,默认 执行 block.startCoroutineCancellable 扩展方法:

internal fun <R, T> (suspend (R) -> T).startCoroutineCancellable(receiver: R, completion: Continuation<T>,onCancellation: ((cause: Throwable) -> Unit)? = null
) =runSafely(completion) {createCoroutineUnintercepted(receiver, completion).intercepted().resumeCancellableWith(Result.success(Unit), onCancellation)}

说明:

  1. createCoroutineUnintercepted, 创建 Continuation;
  2. intercepted,拦截生成 DispatchedContinuation;
  3. resumeCancellableWith,调度器 派发 执行协程。
intercepted()

关键在 intercepted() 方法:

  • 获取 ContinuationInterceptor,默认值是 Dispatchers.Default,
  • 使用 CoroutineDispatcher.interceptContinuation 生成 DispatchedContinuation
// ContinuationImpl 拦截方法
@Transient
private var intercepted: Continuation<Any?>? = nullpublic fun intercepted(): Continuation<Any?> =intercepted?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this).also { intercepted = it }// Dispatchers.Default 源码
public final override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> =DispatchedContinuation(this, continuation)
resumeCancellableWith

DispatchedContinuation.resumeCancellableWith 实现了 调度器(线程池)的任务 派发,也就是 Runnable

inline fun resumeCancellableWith(result: Result<T>,noinline onCancellation: ((cause: Throwable) -> Unit)?
) {val state = result.toState(onCancellation)if (dispatcher.isDispatchNeeded(context)) {_state = stateresumeMode = MODE_CANCELLABLEdispatcher.dispatch(context, this)} else {executeUnconfined(state, MODE_CANCELLABLE) {if (!resumeCancelled(state)) {resumeUndispatchedWith(result)}}}
}

默认执行到
dispatcher.dispatch(context, this),此时调度器 派发 执行。
DispatchedContinuation 实现了 Runnable 接口, run() 调用即开始执行阶段,接下来分析。

2. DispatchedContinuation

DispatchedContinuation 继承 DispatchedTask,委托了 Contanuation

协程的最终执行:

DispatchedContinuation.resumeCancellableWith -> dispatcher.dispatch()
-> DispatchedTask.run() -> DispatchedContinuation.continuation.resumeWith

最终 DispatchedContinuation.continuation 也就是我们 launch {} 块生成的 SuspendLambda 类对象。

ContinuationImpl

无论是 launch {} 块生成的 SuspendLambda类,还是 suspend fun 函数 生成 ContinuationImpl 匿名类。
它们都 继承 BaseContinuationImpl

基类 BaseContinuationImpl,实现了 resumeWith

try {val outcome = invokeSuspend(param)if (outcome === COROUTINE_SUSPENDED) returnResult.success(outcome)
} catch (exception: Throwable) {Result.failure(exception)
}

调用了 抽象 invokeSuspend,也就是 launch {} 块编译后的代码。

执行完成后,会执行 协程 completion.resumeWith(outcome),最终完成。

resumeWith -> onCompletionInternal -> onCompleted 或 onCancelled

launch {} 块编译代码分析

launch {}async {} 编译后,都继承 SuspendLambda

反编译.class,通过 jadx-gui 看到 块 代码一般是下面 形式:

public static final class AnonymousClass1 extends SuspendLambda implements Function2<CoroutineScope, Continuation<? super Unit>, Object> {int label;@NotNullpublic final Continuation<Unit> create(@Nullable Object value, @NotNull Continuation<?> continuation) {return new AnonymousClass1(continuation);}@Nullablepublic final Object invoke(@NotNull CoroutineScope p1, @Nullable Continuation<? super Unit> continuation) {return create(p1, continuation).invokeSuspend(Unit.INSTANCE);}@Nullablepublic final Object invokeSuspend(@NotNull Object $result) {// launch {} 执行代码块 逻辑,会编译到这里...}
}

小结

通过launch启动协程源码分析,了解了:

  • Dispatchers 调度器何时派发任务
  • 协程 Continuation 挂起、执行 和 恢复

补充类图

Continuation
Continuation
context
resumeWith(result)
BaseContinuationImpl
resumeWith(result)
invokeSuspend(result)
ContinuationImpl
SuspendLambda
RestrictedContinuationImpl
RestrictedSuspendLambda

DispatchedContinuation

DispatchedContinuation
dispatcher
continuation
resumeWith(result)
resumeCancellableWith(result)
DispatchedTask
run()
Runnable

关键类和文件

  1. kotlin.coroutines.Continuation.kt
  2. kotlin.coroutines.CoroutineContext
  3. kotlin.coroutines.jvm.internal.BaseContinuationImpl.kt 对应 Continuation 默认实现。
  4. kotlinx.coroutines.CoroutineStart 启动线程方式、调用

文档

  • kotlin coroutines
  • kotlin KEEP | Offcial
  • Advanced coroutines concepts | Android
  • 分析Kotlin协程实现原理
  • 探索 Kotlin 协程原理
  • KotlinConf 2017 - Deep Dive into Coroutines on JVM
  • Koltin 协程(非阻塞式挂起)实现原理(五)
  • kotlinx-coroutines-core 1.7.3源码
  • https://doordash.engineering/2021/11/09/the-beginners-guide-to-kotlin-coroutine-internals/
  • CancellableContinuationImpl.kt

相关文章:

  • 抖动与相噪
  • 【面试】测试/测开(ING3)
  • UI开发布局-HarmonyOS应用UI开发布局
  • 【Python 千题 —— 基础篇】参加聚会
  • 软件测试阶段简介_单元测试、集成测试、配置项测试、系统测试
  • 表的增删改查 进阶(二)
  • MySQL(四)——约束
  • Python GUI 新手入门教程:轻松构建图形用户界面
  • [足式机器人]Part2 Dr. CAN学习笔记- Kalman Filter卡尔曼滤波器Ch05
  • 我用 ChatGPT 做了一次探索性数据分析,真的太太太实用了!
  • 【算法与数据结构】Java实现查找与排序
  • TPU编程竞赛系列|第八届集创赛“算能杯“报名开启!
  • 阿里云服务器配置选择之线下IDC直接映射
  • 【备战蓝桥杯】吃奶酪问题 / 超硬核,文附template拓展知识!
  • 位运算的规则(算法村第十一关青铜挑战)
  • .pyc 想到的一些问题
  • CentOS7简单部署NFS
  • django开发-定时任务的使用
  • input实现文字超出省略号功能
  • JAVA SE 6 GC调优笔记
  • javascript 哈希表
  • laravel5.5 视图共享数据
  • Median of Two Sorted Arrays
  • python3 使用 asyncio 代替线程
  • spark本地环境的搭建到运行第一个spark程序
  • SpiderData 2019年2月16日 DApp数据排行榜
  • Spring-boot 启动时碰到的错误
  • 当SetTimeout遇到了字符串
  • 对超线程几个不同角度的解释
  • 开源地图数据可视化库——mapnik
  • 聊聊flink的BlobWriter
  • 马上搞懂 GeoJSON
  • 前端存储 - localStorage
  • 想使用 MongoDB ,你应该了解这8个方面!
  • Java数据解析之JSON
  • ​RecSys 2022 | 面向人岗匹配的双向选择偏好建模
  • ​决定德拉瓦州地区版图的关键历史事件
  • (Redis使用系列) Springboot 使用redis的List数据结构实现简单的排队功能场景 九
  • (windows2012共享文件夹和防火墙设置
  • (zt)最盛行的警世狂言(爆笑)
  • (四)linux文件内容查看
  • (一)基于IDEA的JAVA基础1
  • (转)eclipse内存溢出设置 -Xms212m -Xmx804m -XX:PermSize=250M -XX:MaxPermSize=356m
  • .MyFile@waifu.club.wis.mkp勒索病毒数据怎么处理|数据解密恢复
  • .NET CORE 3.1 集成JWT鉴权和授权2
  • .NET轻量级ORM组件Dapper葵花宝典
  • //解决validator验证插件多个name相同只验证第一的问题
  • @media screen 针对不同移动设备
  • [20190416]完善shared latch测试脚本2.txt
  • [BZOJ 2142]礼物(扩展Lucas定理)
  • [C++]模板与STL简介
  • [DevOps云实践] 彻底删除AWS云资源
  • [Docker]六.Docker自动部署nodejs以及golang项目
  • [IE编程] IE中使网页元素进入编辑模式
  • [linux运维] 利用zabbix监控linux高危命令并发送告警(基于Zabbix 6)