最近一直在深耕 Kotlin 协程,通过官方文档系统学习 + 个人实践总结,梳理出了一套完整的学习笔记。不得不说,官方文档永远是最权威、最全面的学习资料。
协程官方文档:Coroutines guide | Kotlin
在线运行代码:Kotlin Playground: Edit, Run, Share Kotlin Code Online
我计划用多篇文章,从基础到进阶、从用法到原理,把 Kotlin 协程的核心知识点拆解得明明白白,现在把这份笔记分享出来,希望能给正在学习协程的读者提供一些帮助。
由于个人知识储备有限,笔记中难免存在疏漏或表述不当的地方,也非常欢迎大家提出宝贵意见,一起交流进步
文章中代码块百分之90都可使用运行,一定实操一下
适用人群:Android开发,Kotlin基础
Kotlin 协程前言

Kotlin 协程(Coroutines)官方核心定义
协程是一种轻量级的计算并发方案,允许你在代码中暂停执行(挂起),之后再恢复执行 ------ 且全程不会阻塞底层线程。
它的核心定位是: "非阻塞式的挂起"(Non-blocking suspension) ,是 Kotlin 官方推荐的、用于替代回调(Callback)和线程池,来处理异步任务(如网络请求、数据库操作、延迟执行)的首选方案。
官方强调的核心特性
- 轻量级:协程比线程占用资源少得多(一个线程可承载成千上万个协程)。线程是操作系统级的资源,创建 / 切换开销大;而协程是 Kotlin 语言层的抽象,挂起 / 恢复由语言 runtime 管理,几乎无额外开销。
- 非阻塞挂起 :协程的 "暂停"(通过
suspend关键字标记)不会阻塞线程 ------ 线程在协程挂起时可以去执行其他任务,直到协程需要恢复时再回来继续执行。这和线程的 "阻塞"(如Thread.sleep())完全不同(阻塞会让线程闲置)。 - 结构化并发(Structured Concurrency) :协程必须在
CoroutineScope(作用域)内启动,作用域会管理所有子协程的生命周期 ------ 当作用域被取消时,其下所有协程都会被取消,从根源上避免协程泄漏(比如页面销毁后协程仍在运行)。 - 与现有代码兼容:协程可以无缝对接 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 输出日志
官方明确的关键注意点
- 协程不是线程:协程运行在线程上,可在不同线程间切换,但本身不占用线程资源;
suspend关键字仅标记 "可挂起",不直接实现挂起:真正的挂起逻辑由 Kotlin 协程库(如delay())或第三方库(如 Retrofit 挂起接口)实现;- 协程必须通过
CoroutineBuilder(如launch/async)启动,且依赖kotlinx-coroutines-core库(官方提供的协程实现库,非 Kotlin 标准库内置)。
官方定位总结
Kotlin 协程是 "面向日常开发的、高效且安全的异步并发方案" ------ 它解决了传统线程 / 回调的痛点(开销大、代码丑、易泄漏),让异步编程像同步编程一样简单,同时保持高性能。
如需查看完整官方文档,可访问:Kotlin 协程官方文档
通俗讲 Kotlin 协程
就是一种 "轻量级的虚拟线程" ------ 看着像线程一样能并发干活,但比线程省资源、还不耽误事,核心是帮你把 "绕来绕去的异步代码"(比如网络请求、等数据)写成 "直来直去的同步代码"。
举个生活例子理解:
- 传统线程:像你雇了个工人,他干着活(比如烧水),水没开的时候就站那等着(阻塞),啥也不干,占着人还不干活;
- 协程:还是这个工人,烧水的时候他不傻等,转而去干别的活(比如扫地),水开了再回来继续烧水(恢复)------ 既没浪费人力,又把所有活都干了,这就是 "非阻塞挂起"。
再说说实际用的时候的感觉:
- 以前写异步(比如先请求网络再更 UI),得写回调嵌套(A 做完调 B,B 做完调 C),绕得头晕;
- 用协程:直接按 "先请求网络 → 拿到数据 → 更新 UI" 的顺序写代码,看着跟 "一步做完" 一样,但实际上请求网络的时候线程没闲着,去干别的了,等数据回来再接着执行下一句 ------ 代码简洁,还不浪费资源。
还有两个关键特点通俗说:
- 特别 "轻":一个线程能扛成千上万个协程,不像线程,创建多了就卡;
- 有 "管家"(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% 场景)
构建器是"启动协程的工具",入门先掌握 launch 和 async 即可。
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 点)
不用记复杂表格,记住"协程比线程省、快、好管理"就行:
- 更省资源:一个线程可承载成千上万个协程(协程仅占 KB 级内存),而线程是 MB 级,多了会卡顿。
- 切换更快:协程暂停/恢复是"用户态切换",只需保存执行状态;线程切换是"内核态切换",开销大。
- 更好管理:协程有作用域统一管控,不会泄漏;线程需手动管理线程池、中断等,容易出问题。
类比记忆:线程 = 全职员工(招聘/管理成本高,没事干也得发工资);协程 = 临时工(随用随启,暂停时不占成本,一个全职员工能管一群临时工)。
入门总结:协程使用四步走
按这个流程走,入门就能应对大部分场景:
- 建管理者 :创建作用域 →
val scope = CoroutineScope(调度器); - 启协程 :用构建器启动 →
scope.launch { ... }或scope.async { ... }; - 控协程 :用 Job/Deferred 控制 →
job.cancel()或deferred.await(); - 清资源 :组件销毁时取消作用域 →
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. 通俗解释
作用域是管理协程的"部门经理",所有协程(员工)必须归属于某个作用域(部门)。经理的核心职责:
- 管招聘:只有经理能安排员工(协程)上岗(启动);
- 管裁员:经理离职(作用域取消),手下所有员工(协程)都得停工(取消),避免"员工失控"(协程泄漏)。
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 中lifecycleScope和viewModelScope可自动绑定生命周期,避免泄漏。 - CoroutineDispatcher(调度器) :协程的 "工位分配员",指定运行线程。入门核心是三大调度器:
Main(UI 线程)、IO(网络 / 文件等 IO 密集型任务)、Default(排序 / 计算等 CPU 密集型任务),通过withContext()实现线程灵活切换。 - CoroutineBuilder(构建器) :启动协程的 "招聘工具",核心是
launch(无返回值,返回Job控制协程)和async(有返回值,返回Deferred,通过await()取结果),runBlocking仅用于 main 函数和单元测试,日常开发禁用。
核心优势与使用逻辑
协程通过 "结构化并发" 管控生命周期,非阻 塞挂起提升线程利用率,切换开销远低于线程。使用遵循四步:创建作用域指定调度器、通过launch/async启动协程、用Job/Deferred控制协程、组件销毁时取消作用域。其核心价值是让异步代码线性化,兼顾高性能与可读性,成为 Kotlin 异步编程的首选方案。