Kotlin 协程 快速入门

最近一直在深耕 Kotlin 协程,通过官方文档系统学习 + 个人实践总结,梳理出了一套完整的学习笔记。不得不说,官方文档永远是最权威、最全面的学习资料。

协程官方文档:Coroutines guide | Kotlin

在线运行代码:Kotlin Playground: Edit, Run, Share Kotlin Code Online

我计划用多篇文章,从基础到进阶、从用法到原理,把 Kotlin 协程的核心知识点拆解得明明白白,现在把这份笔记分享出来,希望能给正在学习协程的读者提供一些帮助。

由于个人知识储备有限,笔记中难免存在疏漏或表述不当的地方,也非常欢迎大家提出宝贵意见,一起交流进步

文章中代码块百分之90都可使用运行,一定实操一下

适用人群:Android开发,Kotlin基础

Kotlin 协程前言

Kotlin 协程(Coroutines)官方核心定义

协程是一种轻量级的计算并发方案,允许你在代码中暂停执行(挂起),之后再恢复执行 ------ 且全程不会阻塞底层线程

它的核心定位是: "非阻塞式的挂起"(Non-blocking suspension) ,是 Kotlin 官方推荐的、用于替代回调(Callback)和线程池,来处理异步任务(如网络请求、数据库操作、延迟执行)的首选方案。


官方强调的核心特性

  1. 轻量级:协程比线程占用资源少得多(一个线程可承载成千上万个协程)。线程是操作系统级的资源,创建 / 切换开销大;而协程是 Kotlin 语言层的抽象,挂起 / 恢复由语言 runtime 管理,几乎无额外开销。
  2. 非阻塞挂起 :协程的 "暂停"(通过 suspend 关键字标记)不会阻塞线程 ------ 线程在协程挂起时可以去执行其他任务,直到协程需要恢复时再回来继续执行。这和线程的 "阻塞"(如 Thread.sleep())完全不同(阻塞会让线程闲置)。
  3. 结构化并发(Structured Concurrency) :协程必须在 CoroutineScope(作用域)内启动,作用域会管理所有子协程的生命周期 ------ 当作用域被取消时,其下所有协程都会被取消,从根源上避免协程泄漏(比如页面销毁后协程仍在运行)。
  4. 与现有代码兼容:协程可以无缝对接 Java 的线程池、异步框架(如 Retrofit、Room),支持将传统回调式代码转化为 "线性代码"(避免回调地狱)。

官方给出的核心价值

  • 简化异步代码:把原本嵌套的回调逻辑(如 "请求网络→解析数据→更新 UI")写成线性同步风格的代码,可读性和可维护性大幅提升;
  • 高效利用资源:通过非阻塞挂起减少线程闲置,无需手动管理线程池大小,降低资源浪费;
  • 安全的并发:结构化并发机制确保协程生命周期可控,避免内存泄漏和并发安全问题。

官方最小示例(原文档核心代码)

kotlin 复制代码
import kotlinx.coroutines.*

fun main() = runBlocking { // 启动一个阻塞当前线程的协程作用域(仅用于main函数/测试)
    launch { // 协程构建器:在作用域内启动一个新协程(非阻塞)
        delay(1000) // 挂起1秒(非阻塞,线程可去做其他事)
        println("Hello, Coroutines!") // 1秒后恢复执行
    }
    println("Hello, World!") // 协程挂起时,主线程执行这里(不会等1秒)
}
  • 输出结果:先打印 Hello, World!(立即执行),1 秒后打印 Hello, Coroutines!(协程恢复);
  • 官方想表达的核心:协程挂起不阻塞线程,代码逻辑简洁,无需回调嵌套。

注意:runBlocking 本身带有阻塞线程的意味,但其内部运行的协程又是非阻塞的。

runBlocking 阻塞 "外部线程",但内部协程仍保持非阻塞特性。

kotlin 复制代码
import kotlinx.coroutines.*
import java.lang.Thread

// 简化日志函数,打印线程名和内容
fun log(msg: String) {
    println("[${Thread.currentThread().name}] $msg")
}

fun main() {
    // 1. GlobalScope启动协程(无作用域绑定,独立运行)
    GlobalScope.launch(Dispatchers.IO) {
        delay(600) // 非阻塞挂起600ms(IO线程可干其他活)
	        log("GlobalScope")
    }

    // 2. runBlocking启动协程(阻塞当前主线程)
    runBlocking {
        delay(500) // 内部协程非阻塞挂起500ms
        log("runBlocking")
    }

    // 3. 主线程主动阻塞200ms
    Thread.sleep(200)
    log("after sleep")
}

输出结果(固定顺序)

csharp 复制代码
[main] runBlocking
[DefaultDispatcher-worker-1] GlobalScope
[main] after sleep
//代码中 runBlocking 会早于 GlobalScope 输出日志

官方明确的关键注意点

  1. 协程不是线程:协程运行在线程上,可在不同线程间切换,但本身不占用线程资源;
  2. suspend 关键字仅标记 "可挂起",不直接实现挂起:真正的挂起逻辑由 Kotlin 协程库(如 delay())或第三方库(如 Retrofit 挂起接口)实现;
  3. 协程必须通过 CoroutineBuilder(如 launch/async)启动,且依赖 kotlinx-coroutines-core 库(官方提供的协程实现库,非 Kotlin 标准库内置)。

官方定位总结

Kotlin 协程是 "面向日常开发的、高效且安全的异步并发方案" ------ 它解决了传统线程 / 回调的痛点(开销大、代码丑、易泄漏),让异步编程像同步编程一样简单,同时保持高性能。

如需查看完整官方文档,可访问:Kotlin 协程官方文档


通俗讲 Kotlin 协程

就是一种 "轻量级的虚拟线程" ------ 看着像线程一样能并发干活,但比线程省资源、还不耽误事,核心是帮你把 "绕来绕去的异步代码"(比如网络请求、等数据)写成 "直来直去的同步代码"。

举个生活例子理解:

  • 传统线程:像你雇了个工人,他干着活(比如烧水),水没开的时候就站那等着(阻塞),啥也不干,占着人还不干活;
  • 协程:还是这个工人,烧水的时候他不傻等,转而去干别的活(比如扫地),水开了再回来继续烧水(恢复)------ 既没浪费人力,又把所有活都干了,这就是 "非阻塞挂起"。

再说说实际用的时候的感觉:

  • 以前写异步(比如先请求网络再更 UI),得写回调嵌套(A 做完调 B,B 做完调 C),绕得头晕;
  • 用协程:直接按 "先请求网络 → 拿到数据 → 更新 UI" 的顺序写代码,看着跟 "一步做完" 一样,但实际上请求网络的时候线程没闲着,去干别的了,等数据回来再接着执行下一句 ------ 代码简洁,还不浪费资源。

还有两个关键特点通俗说:

  1. 特别 "轻":一个线程能扛成千上万个协程,不像线程,创建多了就卡;
  2. 有 "管家"(CoroutineScope):所有协程都得归 "管家" 管,你把 "管家" 辞了(取消作用域),它手下的协程全停工,不会出现 "活干完了还在瞎忙活"(协程泄漏)的情况。

总结:协程就是帮你 "高效干异步活、还把代码写得清爽" 的工具,本质是 "让线程不闲着,还不用你手动管切换"。


Kotlin 协程入门指南

协程是 Kotlin 语言层原生支持的非阻塞式并发编程原语,本质是「可暂停、可恢复的函数执行流」。它运行在操作系统线程之上,由 Kotlin 运行时(runtime)管理挂起与恢复逻辑,无需依赖操作系统调度,堪称「用户态的轻量级线程」。

核心机制:非阻塞挂起与恢复(入门必懂)

1. 挂起(Suspension):"暂停但不占线"

  • 触发条件 :当协程执行到带 suspend 关键字的函数(如延时函数 delay()、网络请求接口等)时,会主动释放当前线程的执行权。
  • 核心特性不阻塞线程------线程在协程挂起后,会立刻脱离该协程,去执行其他就绪任务(比如别的协程、普通函数),完全不浪费资源。
  • 通俗原理:挂起时会"保存现场"------记录协程当前的执行位置、局部变量等状态,就像看书时夹书签,后续恢复时能直接从书签处继续。

2. 恢复(Resumption):"回到书签处继续"

  • 触发条件 :挂起函数完成任务后(比如 delay 延时结束、网络请求拿到数据),会通过"续体(Continuation)"通知 Kotlin 协程运行时。
  • 核心特性:协程运行时会"恢复现场"------把之前保存的执行状态恢复到原线程(或指定线程),让协程从挂起点无缝继续执行后续代码。
  • 通俗原理:续体就像"通知器",挂起任务完成后立刻告知协程"可以继续了",协程凭借之前保存的"书签"直接续跑,不用重新从头执行。

关键组件与约束(入门必懂)

1. 核心约束:结构化并发(就记 2 条铁律)

简单说:协程不能"野跑",必须有"管理者" ,这就是结构化并发的核心,从根源避免协程"失控泄漏"。

  • 铁律 1:协程必须在 CoroutineScope(作用域)中启动就像孩子必须有家长监护,协程不能单独启动,得先创建"作用域"这个"管理者"。
kotlin 复制代码
fun main() {
    // 错误:无作用域(管理者),协程无法启动(编译报错)
    // launch { println("我是野协程") }
    // 正确:先创建作用域,再启动协程
    val scope = CoroutineScope(Dispatchers.IO)
    scope.launch {
        println("我是有管理者的协程")
    }
}
  • 铁律 2:作用域销毁,所有协程跟着销毁取消作用域(管理者"离职"),其管理的所有协程都会被强制取消,不会出现"管理者走了,协程还在瞎跑"的泄漏问题。
kotlin 复制代码
fun main() {
    val scope = CoroutineScope(Dispatchers.IO)
    scope.launch {
        delay(10000) // 模拟10秒的耗时任务
        println("任务完成")
    }
    scope.cancel() // 取消作用域,协程立即停止,不会打印结果
}

2. 核心组件(入门记 5 个关键,会用就够)

组件 入门理解(一句话说清) 极简用法示例
suspend 关键字 标记"能暂停的函数",仅可在协程内调用 suspend fun getData() {}
CoroutineScope 协程的"管理者",负责协程的生死 val scope = CoroutineScope(调度器)
CoroutineContext 协程的"配置包",装调度器、异常处理器等 Job() + Dispatchers.IO
CoroutineDispatcher 协程的"工作地点",指定运行线程 Main(UI)、IO(网络/文件)、Default(计算)
Job 协程的"遥控器",可取消、等待协程 val job = scope.launch {} → job.cancel()

3. 组件细节拆解(入门必吃透)

(1)suspend 关键字:"暂停许可"

  • 只有带 suspend 标记的函数,才能实现"暂停"逻辑(比如系统提供的 delay(1000) 就带这个标记)。
  • 普通函数(无 suspend不能调用挂起函数,只有协程体内或其他挂起函数中才能调用。
kotlin 复制代码
// 挂起函数(有暂停许可)
suspend fun delayTask() {
    delay(1000) // 暂停1秒,合法
}

// 普通函数(无暂停许可,不能调用挂起函数)
fun normalFunc() {
    // delayTask() // 错误:普通函数禁止调用挂起函数
}

// 协程内调用(合法,协程有运行挂起函数的权限)
val scope = CoroutineScope(Dispatchers.IO)
scope.launch {
    delayTask() // 正确:协程内可调用挂起函数
}

(2)调度器:"工作地点分配器"

入门只需掌握 3 个常用调度器,覆盖 90% 场景:

  • Dispatchers.Main:UI 线程(如 Android 更新 TextView),单线程。
  • Dispatchers.IO:IO 密集型任务(网络请求、数据库操作、文件读写),线程池。
  • Dispatchers.Default:CPU 密集型任务(排序、循环计算),线程池。

实战常用:用 withContext 临时切换线程,任务完成后自动切回原线程。

kotlin 复制代码
import kotlinx.coroutines.*

fun main() {
    val scope = CoroutineScope(Dispatchers.Main)
    scope.launch { // 初始在UI线程
        // 临时切换到IO线程执行网络请求
        val data = withContext(Dispatchers.IO) {
            fetchDataFromNet() // 网络请求(IO线程执行,不阻塞UI)
        }
        updateUI(data) // 自动切回UI线程,安全更新界面
    }
}

(3)Job:协程"遥控器"

启动协程后会返回 Job 对象,通过它可控制协程的生命周期:

kotlin 复制代码
import kotlinx.coroutines.*

fun main() {
    val scope = CoroutineScope(Dispatchers.IO)
    // 启动协程,拿到"遥控器"
    val job = scope.launch {
        delay(3000) // 模拟3秒任务
        println("协程完成工作")
    }

    // 控制协程(二选一)
    job.cancel() // 取消协程:上面的打印不会执行
    // job.join() // 等待协程:等3秒任务完成后,再执行后续代码
}

协程构建器(入门记 2 个,覆盖 80% 场景)

构建器是"启动协程的工具",入门先掌握 launchasync 即可。

1. launch:无返回值协程("只干活,不拿结果")

  • 适用场景:执行无需返回结果的任务(更新UI、打印日志、发送通知)。
  • 返回值Job(协程遥控器)。
ini 复制代码
val scope = CoroutineScope(Dispatchers.Main)
// 启动协程更新UI(无返回值)
val job = scope.launch {
    val text = "Hello 协程!"
    textView.text = text // 直接更新UI(Main调度器保证线程安全)
}

2. async:有返回值协程("干活+拿结果")

  • 适用场景:执行需要返回结果的任务(网络请求、数据库查询、数据计算)。
  • 返回值Deferred<T>(带返回值的 Job),通过 await() 拿结果。
dart 复制代码
val scope = CoroutineScope(Dispatchers.IO)
// 启动有返回值的协程(计算100+200)
val deferred = scope.async {
    100 + 200 // 计算结果作为返回值
}

// 拿到结果(会等待协程计算完成)
val result = deferred.await() // result = 300
println("计算结果:$result")

入门避坑:runBlocking 仅用 2 个场景

runBlocking阻塞当前线程,仅在以下 2 个场景使用,日常开发(如 Android)绝对不用!

kotlin 复制代码
// 场景1:main函数(程序入口)启动协程
fun main() = runBlocking {
    launch {
        delay(1000)
        println("程序入口的协程执行")
    }
}

// 场景2:单元测试中启动协程
@Test
fun testCoroutine() = runBlocking {
    val result = async { 1 + 1 }.await()
    assertEquals(2, result)
}

协程与线程的核心区别(入门记 3 点)

不用记复杂表格,记住"协程比线程省、快、好管理"就行:

  1. 更省资源:一个线程可承载成千上万个协程(协程仅占 KB 级内存),而线程是 MB 级,多了会卡顿。
  2. 切换更快:协程暂停/恢复是"用户态切换",只需保存执行状态;线程切换是"内核态切换",开销大。
  3. 更好管理:协程有作用域统一管控,不会泄漏;线程需手动管理线程池、中断等,容易出问题。

类比记忆:线程 = 全职员工(招聘/管理成本高,没事干也得发工资);协程 = 临时工(随用随启,暂停时不占成本,一个全职员工能管一群临时工)。

入门总结:协程使用四步走

按这个流程走,入门就能应对大部分场景:

  1. 建管理者 :创建作用域 → val scope = CoroutineScope(调度器)
  2. 启协程 :用构建器启动 → scope.launch { ... }scope.async { ... }
  3. 控协程 :用 Job/Deferred 控制 → job.cancel()deferred.await()
  4. 清资源 :组件销毁时取消作用域 → scope.cancel()

掌握这四步,你就已经跨过了 Kotlin 协程的入门门槛,后续可逐步探索异常处理、协程上下文组合等进阶特性~

Kotlin协程四大核心概念:通俗解释+入门API指南

本文用"公司团队"作类比,把协程的四大核心概念讲透,每个概念都配套入门必用API极简示例,看完就能上手实操。

一、Coroutine(协程):"具体干活的员工"

1. 通俗解释

协程是执行具体业务逻辑的"员工",特点是"能摸鱼不旷工"------干活累了(比如等网络响应)可以暂停休息(挂起),但不占着工位(不阻塞线程),有任务了再回来继续干(恢复)。它是"轻量级员工",一个线程(工位)能容纳成千上万个协程(员工)。

2. 核心关联API(入门必知)

协程本身需通过"构建器"启动,核心关联的API集中在"挂起函数"和"状态判断":

API类别 关键API 作用说明
挂起函数标记 suspend 关键字 标记函数为"可暂停",只有这类函数能在协程内实现挂起逻辑
系统挂起函数 delay(time: Long) 协程暂停指定毫秒数(非阻塞,线程可干其他活)
状态判断 coroutineContext.isActive 判断当前协程是否活跃(未取消、未完成),循环任务中常用

3. 入门示例

kotlin 复制代码
import kotlinx.coroutines.*

// 完整可运行示例:协程基础执行
suspend fun doWork() {
    println("员工开始干活,线程:${Thread.currentThread().name}")
    delay(1000) // 非阻塞暂停1秒
    //if (coroutineContext.isActive) { // 需导入kotlin.coroutines.coroutineContext
    // println("员工摸鱼结束,继续干活,线程:${Thread.currentThread().name}")
    //}
    println("员工摸鱼结束,继续干活,线程:${Thread.currentThread().name}")
}

// 程序入口,必须有main函数(修复语法,确保括号闭合)
fun main() = runBlocking {
    println("主线程启动,线程:${Thread.currentThread().name}")
    launch { // 启动子协程
        doWork()
    }
    println("main函数内协程启动后,线程:${Thread.currentThread().name}")
}

输出结果:

less 复制代码
主线程启动,线程:main @coroutine#1
main函数内协程启动后,线程:main @coroutine#1
员工开始干活,线程:main @coroutine#2
员工摸鱼结束,继续干活,线程:main @coroutine#2

二、CoroutineScope(协程作用域):"部门经理"

1. 通俗解释

作用域是管理协程的"部门经理",所有协程(员工)必须归属于某个作用域(部门)。经理的核心职责:

  1. 管招聘:只有经理能安排员工(协程)上岗(启动);
  2. 管裁员:经理离职(作用域取消),手下所有员工(协程)都得停工(取消),避免"员工失控"(协程泄漏)。

2. 核心API(入门必用)

API类别 关键API 作用说明
创建作用域 CoroutineScope(context: CoroutineContext) 最核心构造方法,传入"配置"创建作用域(经理上岗)
取消作用域 scope.cancel() 取消作用域及旗下所有协程(经理离职,全员停工)
常用预设作用域 lifecycleScope(Android) 绑定Activity/Fragment生命周期,自动取消(不用手动调用cancel)
常用预设作用域 viewModelScope(Android) 绑定ViewModel生命周期,ViewModel销毁时自动取消
作用域属性 scope.coroutineContext 获取作用域的配置信息(如调度器、Job)

3. 入门示例

kotlin 复制代码
import kotlinx.coroutines.*

// 完整可运行示例:协程作用域管理
fun main() {
    println("主线程启动,线程:${Thread.currentThread().name}")
    // 1. 创建作用域(指定IO调度器+独立Job)
    val workScope = CoroutineScope(Dispatchers.IO + Job())

    // 2. 启动协程执行任务
    workScope.launch {
        println("协程开始任务,线程:${Thread.currentThread().name}")
        delay(2000) // 模拟2秒耗时任务
        println("协程完成任务,线程:${Thread.currentThread().name}") // 此句不会执行
    }

    // 3. 主线程等待1秒后取消作用域
    Thread.sleep(1000) // 主线程阻塞等待,观察协程状态
    workScope.cancel()
    println("作用域已取消,所有协程停止,线程:${Thread.currentThread().name}")

    // 主线程再等1秒,确认协程已停止
    Thread.sleep(1000)
    println("程序结束,线程:${Thread.currentThread().name}")
}

输出结果:

less 复制代码
主线程启动,线程:main
协程开始任务,线程:DefaultDispatcher-worker-1 @coroutine#1
// 间隔1秒后
作用域已取消,所有协程停止,线程:main
// 再间隔1秒后
程序结束,线程:main

三、CoroutineDispatcher(协程调度器):"工位分配员"

1. 通俗解释

调度器是给协程(员工)分配"工位"(线程)的专员,决定员工在哪类工位上干活。不同工位有不同用途,比如"前台工位"(主线程)只能接待客户(更新UI),"后台工位"(IO线程)适合处理杂活(网络请求)。

2. 核心API(入门必用3个)

核心调度器 对应"工位" 适用场景 配套API
Dispatchers.Main 前台工位(主线程) UI操作(如Android更新TextView、iOS刷新界面) 直接作为作用域/构建器参数
Dispatchers.IO 后台杂活工位(IO线程池) 网络请求、数据库操作、文件读写、日志打印 直接作为参数,或用withContext临时切换
Dispatchers.Default 技术工位(CPU线程池) CPU密集型任务(排序、循环计算、数据解析) 直接作为参数
线程切换工具 工位临时调换 协程内临时切换线程,完成后自动切回 withContext(dispatcher) { ... }

3. 入门示例

kotlin 复制代码
import kotlinx.coroutines.*

// 完整可运行示例:协程调度器线程切换(JVM通用版)
fun main() = runBlocking(Dispatchers.Default) {
    // runBlocking指定Default调度器(JVM环境通用,对应CPU密集型线程池)
    println("初始线程:${Thread.currentThread().name}(对应Dispatchers.Default)")

    // 临时切换到IO调度器执行任务(网络/文件/数据库等IO密集型操作)
    val data = withContext(Dispatchers.IO) {
        println("切换到IO线程:${Thread.currentThread().name}(对应Dispatchers.IO)")
        delay(1000) // 模拟1秒网络请求或文件读取任务
        "用户信息:Kotlin协程入门" // 任务执行结果
    }

    // 自动切回初始调度器(Dispatchers.Default)
    println("切回原线程:${Thread.currentThread().name},获取数据:$data")
}

输出结果:

perl 复制代码
初始线程:DefaultDispatcher-worker-1 @coroutine#1(对应Dispatchers.Default)
切换到IO线程:DefaultDispatcher-worker-2 @coroutine#1(对应Dispatchers.IO)
// 间隔1秒后
切回原线程:DefaultDispatcher-worker-3 @coroutine#1,获取数据:用户信息:Kotlin协程入门

四、CoroutineBuilder(协程构建器):"员工招聘工具"

1. 通俗解释

构建器是"招聘工具",由作用域(经理)使用,用来招募不同类型的协程(员工)。比如有的工具招"只干活不汇报结果"的员工(launch),有的招"干活后要交成果"的员工(async)。

2. 核心API(入门必掌握2个)

构建器 招募"员工"类型 返回值(工具产出) 核心API 适用场景
launch 无成果汇报型 Job(员工遥控器) scope.launch(context, start) { ... } 更新UI、发送通知、执行无返回值任务
async 有成果汇报型 Deferred<T>(带成果的遥控器) scope.async(context, start) { ... }deferred.await() 网络请求、数据库查询、数据计算(需返回结果)
runBlocking 测试/入口专用型 T(协程返回值) runBlocking(context) { ... } main函数、单元测试(桥接阻塞与非阻塞代码)

注意:runBlocking 会阻塞当前线程,仅用于 main 函数(程序入口)和单元测试,Android/iOS等应用开发中绝对不能在UI线程使用!

3. 入门示例

示例1:launch(无返回值,用Job控制)

kotlin 复制代码
import kotlinx.coroutines.*
import java.lang.Thread

// 完整可运行示例:launch构建器(无返回值)
fun main() = runBlocking {
    println("主线程启动,线程:${Thread.currentThread().name}")
    // 创建作用域(JVM环境改用Default调度器,避免Main缺失问题)
    val scope = CoroutineScope(Dispatchers.Default)

    // 1. 用launch启动无返回值协程,获取Job对象
    val job = scope.launch {
        println("launch协程开始,线程:${Thread.currentThread().name}")
        delay(1000) // 模拟1秒任务
        println("launch协程完成,线程:${Thread.currentThread().name}")
    }

    // 2. 主线程等待协程完成(可选操作)
    job.join() // 挂起主线程,等待协程执行完毕
    println("协程已完成,准备取消作用域,线程:${Thread.currentThread().name}")

    // 3. 取消作用域释放资源
    scope.cancel()
    println("程序结束,线程:${Thread.currentThread().name}")
}

输出结果:

perl 复制代码
主线程启动,线程:main @coroutine#1
launch协程开始,线程:DefaultDispatcher-worker-1 @coroutine#2
launch协程完成,线程:DefaultDispatcher-worker-1 @coroutine#2
协程已完成,准备取消作用域,线程:main @coroutine#1
程序结束,线程:main @coroutine#1

示例2:async(有返回值,用await拿成果)

kotlin 复制代码
import kotlinx.coroutines.*
import java.lang.Thread

// 完整可运行示例:async构建器(有返回值)
fun main() = runBlocking {
    println("主线程启动,线程:${Thread.currentThread().name}")
    // 创建作用域(指定IO调度器)
    val scope = CoroutineScope(Dispatchers.IO)

    // 1. 用async启动有返回值协程,获取Deferred对象
    val dataDeferred = scope.async {
        println("async协程开始计算,线程:${Thread.currentThread().name}")
        delay(1000) // 模拟1秒计算任务
        val result = 100 + 200 // 计算结果
        println("async协程计算完成,结果:$result,线程:${Thread.currentThread().name}")
        result // 返回结果
    }

    // 2. 用await()获取结果(会挂起当前协程,不阻塞线程)
    val finalResult = dataDeferred.await()
    println("主线程获取结果:$finalResult,线程:${Thread.currentThread().name}")

    // 3. 取消作用域
    scope.cancel()
    println("程序结束,线程:${Thread.currentThread().name}")
}

输出结果:

perl 复制代码
主线程启动,线程:main @coroutine#1
async协程开始计算,线程:DefaultDispatcher-worker-1 @coroutine#2
async协程计算完成,结果:300,线程:DefaultDispatcher-worker-3 @coroutine#2
主线程获取结果:300,线程:main @coroutine#1
程序结束,线程:main @coroutine#1

五、四大概念关联总结(一句话串懂)

CoroutineScope(部门经理)使用 CoroutineBuilder(招聘工具)招募 Coroutine(员工),并由 CoroutineDispatcher(工位分配员)给员工分配合适的工位(线程),员工干活时能暂停休息(挂起),经理离职(作用域取消)则全员停工。

六、入门实战流程图(必背)

kotlin 复制代码
import kotlinx.coroutines.*
import java.lang.Thread

// 完整可运行示例:协程实战四步走
fun main() = runBlocking {
    println("程序启动,主线程:${Thread.currentThread().name}")
    
    // 1. 第一步:创建作用域(经理)+ 分配调度器(工位类型)
    val businessScope = CoroutineScope(Dispatchers.IO) // 所有协程默认在IO线程执行
    
    // 2. 第二步:用构建器启动协程(招聘员工)
    // 场景A:无返回值任务(如日志打印、状态更新)
    val logJob = businessScope.launch {
        println("场景A-无返回值协程启动,线程:${Thread.currentThread().name}")
        delay(1000) // 模拟1秒任务
        println("场景A-无返回值协程完成,线程:${Thread.currentThread().name}")
    }
    
    // 场景B:有返回值任务(如数据计算、网络请求)
    val calcDeferred = businessScope.async {
        println("场景B-有返回值协程启动,线程:${Thread.currentThread().name}")
        delay(1500) // 模拟1.5秒计算任务
        val result = 50 * 6 // 计算逻辑
        println("场景B-有返回值协程完成,结果:$result,线程:${Thread.currentThread().name}")
        result // 返回计算结果
    }
    
    // 3. 第三步:控制协程(等待任务完成/取消)
    logJob.join() // 等待场景A协程完成
    val calcResult = calcDeferred.await() // 获取场景B协程结果
    println("场景B-主线程拿到结果:$calcResult,线程:${Thread.currentThread().name}")
    
    // 4. 第四步:收尾-取消作用域(释放所有资源)
    businessScope.cancel()
    println("作用域已取消,所有协程停止,程序结束,线程:${Thread.currentThread().name}")
}

输出结果:

less 复制代码
程序启动,主线程:main @coroutine#1
场景B-有返回值协程启动,线程:DefaultDispatcher-worker-2 @coroutine#3
场景A-无返回值协程启动,线程:DefaultDispatcher-worker-1 @coroutine#2
场景A-无返回值协程完成,线程:DefaultDispatcher-worker-1 @coroutine#2
场景B-有返回值协程完成,结果:300,线程:DefaultDispatcher-worker-1 @coroutine#3
场景B-主线程拿到结果:300,线程:main @coroutine#1
作用域已取消,所有协程停止,程序结束,线程:main @coroutine#1

Kotlin 协程核心概念总结

Kotlin 协程是语言层原生的非阻塞式并发原语,核心是 "可暂停、可恢复的轻量级执行流",通过四大核心组件实现高效异步编程,彻底解决传统线程与回调的痛点。

核心组件与核心作用

  • Coroutine(协程) :执行具体逻辑的 "轻量员工",可挂起不阻塞线程,一个线程能承载成千上万个协程。通过suspend标记的函数实现挂起,delay()是典型系统挂起函数,运行时保存执行上下文,恢复后无缝续跑。
  • CoroutineScope(作用域) :协程的 "部门经理",所有协程必须归属于作用域。核心 API 包括CoroutineScope(context)创建作用域、scope.cancel()取消作用域及旗下所有协程,Android 中lifecycleScopeviewModelScope可自动绑定生命周期,避免泄漏。
  • CoroutineDispatcher(调度器) :协程的 "工位分配员",指定运行线程。入门核心是三大调度器:Main(UI 线程)、IO(网络 / 文件等 IO 密集型任务)、Default(排序 / 计算等 CPU 密集型任务),通过withContext()实现线程灵活切换。
  • CoroutineBuilder(构建器) :启动协程的 "招聘工具",核心是launch(无返回值,返回Job控制协程)和async(有返回值,返回Deferred,通过await()取结果),runBlocking仅用于 main 函数和单元测试,日常开发禁用。

核心优势与使用逻辑

协程通过 "结构化并发" 管控生命周期,非阻 塞挂起提升线程利用率,切换开销远低于线程。使用遵循四步:创建作用域指定调度器、通过launch/async启动协程、用Job/Deferred控制协程、组件销毁时取消作用域。其核心价值是让异步代码线性化,兼顾高性能与可读性,成为 Kotlin 异步编程的首选方案。

相关推荐
金鸿客2 小时前
用Compose实现一个Banner轮播组件
android
南雨北斗2 小时前
kotlin开发中的构建工具gradle
后端
xuejianxinokok2 小时前
深入了解RUST迭代器 - 惰性、可组合的处理
后端·rust
狂团商城小师妹2 小时前
JAVA国际版同城服务同城信息同城任务发布平台APP源码Android + IOS
android·java·ios
后端小张2 小时前
【JAVA 进阶】Spring Boot 自动配置原理与自定义 Starter 实战
java·spring boot·后端·spring·spring cloud·自定义·原理
想用offer打牌2 小时前
修复seata的HikariCP中加载驱动程序类的问题
后端·架构·开源
q***18842 小时前
搭建Golang gRPC环境:protoc、protoc-gen-go 和 protoc-gen-go-grpc 工具安装教程
开发语言·后端·golang
迷途码界2 小时前
VirtualBox 高版本无法安装在非C盘的问题
后端
爱叫啥叫啥2 小时前
c语言基础:多级指针、函数的基本用法、预处理#define
后端