朱涛 · Kotlin 编程第一课
朱涛
Google 认证的 Kotlin、Android 开发者专家,博客“Kotlin Jetpack 实战”作者
6717 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 50 讲
朱涛 · Kotlin 编程第一课
15
15
1.0x
00:00/00:00
登录|注册

16 | Job:协程也有生命周期吗?

你好,我是朱涛。今天我们来学习 Kotlin 协程的 Job。
Job 其实就是协程的句柄。从某种程度上讲,当我们用 launch 和 async 创建一个协程以后,同时也会创建一个对应的 Job 对象。另外,Job 也是我们理解协程生命周期结构化并发的关键知识点。通过 Job 暴露的 API,我们还可以让不同的协程之间互相配合,从而实现更加复杂的功能。
虽然前面已经解释过,Job 就是协程的句柄,但你可能还是不清楚它到底是什么,因为句柄本身就是一个比较“虚”的概念。所以在这节课中,我们会从使用的角度入手,来看看 Job 到底能干什么。在充分理解了 Job 的用法以后,我们再来结合它的源代码进一步分析,这样对 Job 也会有一个更加清晰的认知。

Job 生命周期

在上节课我们学习 launch、async 的时候,我们知道它们两个返回值类型分别是 Job 和 Deferred。
// 代码段1
public interface Deferred<out T> : Job {
public suspend fun await(): T
}
而如果你去看 Deferred 的源代码,你会发现,它其实也是继承自 Job 的。对应的,它只是多了一个泛型参数 T,还多了一个返回类型为 T 的 await() 方法。所以,不管是 launch 还是 async,它们本质上都会返回一个 Job 对象
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

Kotlin协程的Job是协程的句柄,通过它暴露的API可以实现协程的生命周期管理和结构化并发。本文从使用的角度入手,介绍了Job的功能和用法,为读者提供了对Job的清晰认知。通过示例代码和详细解释,展示了Job的生命周期、状态查询、操控和等待状态等API的使用方法,以及如何监听协程结束事件。同时,通过对Job源代码的分析,读者可以更好地理解其内部实现。文章还介绍了Deferred接口,它继承自Job,提供了await()方法用于获取协程的执行结果。此外,文章还探讨了Job与结构化并发的关系,强调了协程的父子关系和结构化并发的重要性。通过具体的例子和代码分析,读者可以更好地理解协程的结构化并发特点。除此之外,作者还分享了学习协程的方法论,包括横向对比、建立思维模型和纵向深入,为读者提供了学习协程的有效途径。整体而言,本文为读者提供了全面的Job知识,帮助他们更好地理解和应用Kotlin协程中的Job对象。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《朱涛 · Kotlin 编程第一课》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(27)

  • 最新
  • 精选
  • 面无表情的生鱼片
    思考题: 代码的执行结果是: > First coroutine start! > First coroutine end! > Process end! 可见 job2 的代码块并没有被执行。 分析原因: 分别打印出 job2 在 job2.join() 前后的状态: job2 before join: isActive === false job2 before join: isCancelled === true job2 before join: isCompleted === false // job2.join() job2 after join: isActive === false job2 after join: isCancelled === true job2 after join: isCompleted === true 可见 job2 创建后并没有被激活。 val job2 = launch(job) {} 这一行代码指示 job2 将运行在 job 的 CoroutineContext 之下, 而之前的代码 job.join() 时 job 已经执行完毕了,根据协程结构化的特性,job2 在创建后不会被激活,并且标记为Cancelled,然后执行 job2 时,发现 job2 未被激活,并且已经被取消,则不会执行 job2 的代码块,但是会将 job2 标记为 Completed

    作者回复: 很棒的分析!

    2022-02-18
    3
    34
  • 没名儿
    看了大家的留言有个疑点 ----很多异步任务之间都是没有互相依赖的,这样的代码结合挂起函数后,再通过 async 并发来执行,是可以大大提升代码运行效率的。---- ----如你所说,存在依赖关系的时候,我们就可以挂起函数与async结合了。----- 到底是存在依赖关系用async还是不存在依赖关系用async呢?

    作者回复: “如你所说,存在依赖关系的时候,我们就可以挂起函数与async结合了。” 这句话确实有歧义,这里的语境是:如果B任务依赖A的结果,C任务跟A、B没有关系,我们就可以这样做: ``` // 伪代码 runBlocking { val resultB = async { val temp = taskA() taskB(temp) } val resultC = async { taskC() } } ``` 总结就是:如果存在依赖关系,就直接用挂起函数串联;如果没有依赖关系,就可以用async并联。

    2022-03-17
    11
  • 白乾涛
    思考题针对性不强。 因为思考题考察的知识点是 CoroutineContext 上下文,而这一部分是下一节课的内容。

    作者回复: 本质还是“生命周期”、“结构化并发”哈,当然,本质还是CoroutineContext,这不刚好引出下节课嘛~

    2022-02-23
    6
  • 魏全运
    思考题结果: First coroutine start! First coroutine end! Process end! 没有执行job2的原因是,它的launch中传入了job 作为coroutinecontext,而它已经是complete 状态了,所以不会再执行job2的block 而是直接执行了job2的join ,然后结束。

    作者回复: 分析的不错。

    2022-02-18
    6
  • 原仲
    代码片段14 中的执行流程 val result1 = async { getResult1() } val result2 = async { getResult2() } val result3 = async { getResult3() } //调用时机在这 results = listOf(result1.await(), result2.await(), result3.await()) 用作者的思维模型,相当于三个钓鱼杆同时拉杆 val result1 = async { getResult1() } //调用时机 result1.await() val result2 = async { getResult2() } //调用时机 result2.await() val result3 = async { getResult3() } //调用时机 result3.await() 用作者的思维模型,相当于三个钓鱼杆依次拉杆

    作者回复: 赞!

    2022-05-14
    2
    2
  • 20220106
    代码段10中【delay(500L)】这一句影响了什么呀?不加的话后边的日志就不打印了

    作者回复: 它的作用是等待:job1/2/3对应的协程能够启动。

    2022-04-13
    2
    2
  • Gavin
    "First coroutine start!" "First coroutine end!" "Process end!" 通过源码可知launch中传入的CoroutineContext会作为parentJob,而job2的parentJob为job,job协程已经处于completed状态,故不执行job2直接跳过

    作者回复: 没错~

    2022-02-23
    2
  • 20220106
    这里 await() 后面的代码,虽然看起来是阻塞了,但它只是执行流程被挂起和恢复的一种表现。而且如果你仔细思考的话,你会发现上面这个动图,同样也描述了之前 job.join() 的行为模式,在协程执行完毕之前,后面的协程代码都被暂时挂起了,等到协程执行完毕,才有机会继续执行。 ——”在协程执行完毕之前“这里的协程指的父级协程,“后面的协程代码都被暂时挂起了“这里的协程代码指的子级协程代码部份。也就是说:如果子级自己有挂起操作,那么子级的代码会被暂时挂起,直到父级的协程代码执行完毕之后再继续执行子级协程代码(前提是父级没有挂起延迟之类的操作)。

    作者回复: 是这个意思。

    2022-04-14
    1
  • dawn
    fun main() = runBlocking { val job = launch { logX("Coroutine start!") delay(1000L) logX("Coroutine end!") } job.log() delay(500) job.cancel() job.log() delay(1) //延时1ms job.log() delay(2000) logX("Process end!") } 为什么取消后输出延时1ms输出job的isCompleted会有false变为true

    作者回复: 单纯只是因为Job的状态变更没那么及时而已。

    2022-03-30
    1
  • 张国庆
    最后问题应该是按顺序打印

    作者回复: 你可以试着运行一下看看效果,不要忽略了“launch(job) {}”当中的job参数哦!

    2022-02-18
    1
收起评论
显示
设置
留言
27
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部