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

11-Kotlin高阶特性-协程

Kotlin 协程(Coroutines)全面解析

协程是 Kotlin 提供的轻量级并发编程框架,它允许你以顺序的方式编写异步代码,从而避免回调地狱,并大幅简化并发任务的管理。协程不是线程,但可以运行在线程之上,通过挂起(suspend)机制实现高效的并发:挂起时释放底层线程,恢复后续续执行,整个过程非阻塞且开销极小。


一、协程核心概念

1. 什么是协程?

协程是轻量级的线程,由 Kotlin 运行时管理,而非操作系统。与线程相比:

特性线程协程
资源消耗较重(每个线程占用 MB 级内存)极轻(可启动数十万个)
切换开销操作系统上下文切换用户态挂起/恢复,开销极小
并发模型抢占式协作式(通过挂起点主动让出)

2. 核心术语

术语含义
suspend标记「可挂起函数」,仅能在协程或其他挂起函数中调用
CoroutineScope协程作用域,管理协程生命周期(如取消协程、控制协程范围)
Dispatcher协程调度器,指定协程运行的线程(如Dispatchers.Main/IO/Default
Job协程的句柄,可取消协程、监听协程状态(完成/取消/异常)
Deferred带返回值的Job,通过await()获取协程执行结果
CoroutineContext协程上下文,包含调度器、Job、异常处理器等信息

3. 核心概念关系图

flowchart TD A[CoroutineScope<br/>协程作用域] --> B[CoroutineContext<br/>协程上下文] B --> C[Job<br/>任务句柄] B --> D[CoroutineDispatcher<br/>调度器] B --> E[CoroutineExceptionHandler<br/>异常处理器] F[launch<br/>启动协程] --> G[返回 Job<br/>无返回值] H[async<br/>启动协程] --> I[返回 Deferred<T><br/>带返回值] J[suspend 挂起函数] --> K[可调用其他挂起函数] K --> L[delay/yield/withContext]

4. 挂起函数(Suspending Function)

挂起函数是协程的核心抽象,用suspend关键字标记,可以在协程中暂停执行而不阻塞线程,并在稍后恢复。

suspend fun fetchUserData(): String { // 模拟网络请求(非阻塞) delay(1000L) // delay 是一个挂起函数 return "User data" }

关键特性

  • 只能在协程或其他挂起函数中调用;
  • 挂起时释放底层线程,线程可执行其他协程;
  • 恢复后续续执行,仿佛从未暂停。

5. 依赖引入

使用协程前,需要在项目中添加kotlinx-coroutines-core依赖(以 Gradle Kotlin DSL 为例):

dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.8.0") // Android 平台还需添加 android 模块 implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0") // JVM 平台可选(如 jdk8 模块) implementation("org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:1.8.0") }

二、协程基础用法

1. 第一个协程程序

import kotlinx.coroutines.* fun main() = runBlocking { // 启动主协程 launch { // 启动新协程 delay(1000L) // 非阻塞等待 println("World!") } println("Hello,") // 主协程继续执行 delay(2000L) // 等待协程完成 } // 输出: // Hello, // World!
  • runBlocking:创建协程作用域并阻塞当前线程,仅推荐在 main 函数/测试中使用;
  • delay():挂起函数,暂停协程但不阻塞线程。

2. 协程构建器

构建器作用返回值
launch启动新协程,不返回结果Job
async启动新协程,返回一个可等待的结果Deferred<T>
runBlocking阻塞当前线程直到协程完成T
(1)launch:无返回值的协程
fun main() = runBlocking { println("主线程:${Thread.currentThread().name}") val job1 = launch { println("协程1:${Thread.currentThread().name}") delay(1000) println("协程1执行完成") } // 指定调度器:Dispatchers.Default(后台计算线程) val job2 = launch(Dispatchers.Default) { println("协程2:${Thread.currentThread().name}") delay(500) println("协程2执行完成") } job1.join() job2.join() println("所有协程执行完成") }
(2)async:带返回值的协程
fun main() = runBlocking { val deferred1 = async { calculateSum(1, 100) } val deferred2 = async { calculateSum(101, 200) } val result1 = deferred1.await() val result2 = deferred2.await() println("总和:${result1 + result2}") // 20100 } suspend fun calculateSum(start: Int, end: Int): Int { delay(500) var sum = 0 for (i in start..end) sum += i return sum }
(3)runBlocking:阻塞式协程(仅用于桥接)

runBlocking会阻塞当前线程,禁止在生产代码中使用(如 Android 主线程)。

3. 挂起函数(suspend)的调用规则

suspend fun fetchData(url: String): String { delay(1000) return "Data from $url" } fun main() = runBlocking { // 在协程中调用挂起函数 launch(Dispatchers.IO) { val data = fetchData("https://example.com") println(data) } }

三、协程调度器(Dispatcher)

调度器决定协程运行的线程,核心类型:

调度器适用场景线程特性
Dispatchers.MainAndroid 主线程/UI 线程单线程(仅 Android 可用)
Dispatchers.IO网络/文件 IO、数据库操作线程池(按需创建,最多 64 个)
Dispatchers.DefaultCPU 密集型计算(如排序、解析)线程池(核心数 = CPU 核心数)
Dispatchers.Unconfined无固定线程先在当前线程执行,挂起后切换
newSingleThreadContext("name")专属单线程自定义命名的单线程(需手动关闭)

切换调度器:withContext

withContext允许在不启动新协程的情况下切换协程上下文,常用于在 IO 线程执行耗时操作后切换回主线程。

suspend fun fetchUserData(): String = withContext(Dispatchers.IO) { delay(1000L) "User data" } fun main() = runBlocking { val data = fetchUserData() println(data) }

四、协程生命周期管理

1. Job 与取消协程

launch返回的Job对象可管理协程:

  • job.start():启动协程(默认自动启动);
  • job.cancel():取消协程;
  • job.join():等待协程完成;
  • job.cancelAndJoin():取消并等待完成;
  • job.isActive:判断协程是否活跃。
fun main() = runBlocking { val job = launch { repeat(10) { i -> println("协程执行中:$i") delay(500) } } delay(1500) println("取消协程") job.cancelAndJoin() println("协程已取消") }

2. 结构化并发:CoroutineScope

CoroutineScope用于管理多个协程的生命周期,核心规则:

  • 作用域取消时,所有子协程自动取消;
  • Android 中常用lifecycleScope(生命周期绑定)、viewModelScope(ViewModel 绑定)。
fun main() { // 创建自定义作用域 val scope = CoroutineScope(Dispatchers.IO) scope.launch { repeat(5) { println("协程1:$it") delay(500) } } scope.launch { repeat(5) { println("协程2:$it") delay(500) } } // 等待 1 秒后取消作用域(所有子协程取消) Thread.sleep(1000) println("取消作用域") scope.cancel() }

3. 作用域构建器

构建器类型行为
runBlocking常规函数阻塞当前线程直到完成
coroutineScope挂起函数挂起当前协程,创建子作用域,释放线程
supervisorScope挂起函数类似coroutineScope,但子协程失败不影响兄弟协程
fun main() = runBlocking { coroutineScope { launch { delay(1000L) println("Task from nested scope") } delay(100L) println("Task from coroutine scope") } println("Scope is over") }

4. 父子 Job 的层次关系

  • 父协程取消时,所有子协程自动取消;
  • 子协程异常时,默认会向上传播取消父协程;
  • 使用supervisorScope可以改变异常传播行为。

五、异常处理

1. 异常的传播

  • launch:异常立即抛出,可以通过CoroutineExceptionHandler处理;
  • async:异常在调用.await()时抛出,需要 try-catch 处理。
fun main() = runBlocking { // launch 异常处理 val job = launch { try { throw RuntimeException("Error in launch") } catch (e: Exception) { println("Caught: ${e.message}") } } // async 异常处理 val deferred = async { throw RuntimeException("Error in async") } try { deferred.await() } catch (e: Exception) { println("Caught: ${e.message}") } }

2. CoroutineExceptionHandler

全局异常处理器,用于捕获未处理的异常:

val handler = CoroutineExceptionHandler { _, exception -> println("Caught: $exception") } fun main() = runBlocking { val job = launch(handler) { throw RuntimeException("Something went wrong") } job.join() }

3. 监督作用域(Supervision)

普通作用域中,子协程失败会取消父协程和兄弟协程。监督作用域改变了这一行为:

fun main() = runBlocking { supervisorScope { val child1 = launch { try { delay(Long.MAX_VALUE) } finally { println("Child 1 cancelled") } } val child2 = launch { throw RuntimeException("Child 2 failed") } child2.join() child1.cancel() // 需要手动取消 } }

六、协程进阶用法

1. Flow:异步数据流

Flow是协程的异步数据流,支持冷流、操作符链式调用,替代传统回调/回调流。

import kotlinx.coroutines.flow.* fun getNumbers(): Flow<Int> = flow { for (i in 1..5) { delay(500) emit(i) // 发射数据 } } fun main() = runBlocking { getNumbers() .filter { it % 2 == 0 } .map { it * 10 } .collect { println("接收数据:$it") } } // 输出: // 接收数据:20 // 接收数据:40
特性ChannelFlow
类型热流(hot)冷流(cold)
数据生产独立于消费随收集而生产
适用场景协程间通信异步数据序列

2. Channel:协程间的管道

Channel允许在不同协程之间传递数据流:

import kotlinx.coroutines.channels.Channel fun main() = runBlocking { val channel = Channel<Int>() launch { for (x in 1..5) { channel.send(x * x) } channel.close() } for (y in channel) { println(y) } }

3. 合并多个异步源

  • combine:合并两个 Flow
  • async/await:合并多个 Deferred
fun main() = runBlocking { // 合并两个 Flow val flow1 = flow { emit(1); delay(100); emit(2) } val flow2 = flow { emit("A"); delay(200); emit("B") } combine(flow1, flow2) { num, str -> "$num-$str" } .collect { println(it) } // 合并两个 async 结果 val deferred1 = async { 10 } val deferred2 = async { 20 } println("合并结果:${deferred1.await() + deferred2.await()}") }

4. 超时处理:withTimeout

设置协程执行超时时间:

fun main() = runBlocking { try { withTimeout(1000) { repeat(3) { println("执行中:$it") delay(600) } } } catch (e: TimeoutCancellationException) { println("协程超时") } }

5. SharedFlow 与 StateFlow

用于在多个收集器之间共享状态:

  • StateFlow:持有一个可观察的状态值(类似 LiveData)
  • SharedFlow:可配置的事件流

七、协程的性能原理

1. 阻塞 vs 非阻塞

suspend fun blockingWork() { Thread.sleep(1000) // 阻塞当前线程(错误用法) } suspend fun nonBlockingWork() { delay(1000) // 挂起协程,释放线程 }

当使用delay时,协程挂起并释放线程,线程可以去执行其他协程。这是协程高效并发的基础。

2. 协程的轻量性验证

fun main() = runBlocking { repeat(100_000) { launch { delay(1000L) print(".") } } } // 内存消耗极小,运行流畅

如果用线程实现同样的逻辑,很可能会导致内存不足。


八、最佳实践

  1. 避免使用GlobalScope:应使用自定义作用域或结构化并发;
  2. 为主线程安全设计:使用withContext(Dispatchers.IO)执行耗时操作;
  3. 合理选择调度器
    • UI 操作:Dispatchers.Main
    • CPU 密集型:Dispatchers.Default
    • IO 密集型:Dispatchers.IO
  4. 使用超时保护withTimeout防止协程无限运行;
  5. 适当处理取消:计算密集型的协程应定期检查isActive或调用yield()
  6. 使用监督作用域处理独立任务:一个子任务失败不应影响其他任务时;
  7. Android 开发优先使用lifecycleScope/viewModelScope

九、官方资料链接

  1. Kotlin 协程官方文档(英文):https://kotlinlang.org/docs/coroutines.html
  2. Kotlin 协程中文文档:https://www.kotlincn.net/docs/reference/coroutines.html
  3. Kotlin 协程核心指南(官方博客):https://blog.jetbrains.com/kotlin/2019/02/kotlin-1-3-coroutines/
  4. Flow 官方文档:https://kotlinlang.org/docs/flow.html
  5. Android 协程最佳实践:https://developer.android.com/kotlin/coroutines
  6. 通道(Channels):https://kotlinlang.org/docs/channels.html
  7. 异常处理:https://kotlinlang.org/docs/exception-handling.html

总结

  1. 核心概念:协程是轻量级并发框架,通过suspend实现非阻塞挂起,Dispatchers灵活切换线程,以同步写法实现异步逻辑。
  2. 基本使用launch启动无返回值的协程,async返回可等待的结果,runBlocking桥接普通代码。
  3. 结构化并发:协程作用域管理协程生命周期,父子 Job 形成层次结构,自动传播取消和异常。
  4. 异常处理CoroutineExceptionHandler捕获未处理异常,supervisorScope改变异常传播。
  5. 协程通信Channel用于协程间通信,Flow处理异步数据流,StateFlow共享状态。
  6. 性能原理:挂起释放线程,非阻塞等待实现高效并发,可启动数十万协程。

掌握协程是 Kotlin 进阶开发的关键一步,它让你能够以直观的顺序代码处理复杂的异步场景,大幅提升应用性能和可维护性。

http://www.jsqmd.com/news/501740/

相关文章:

  • 天虹购物卡回收小技巧 - 团团收购物卡回收
  • 2026海南GEO优化服务商推荐排名发布:念奴娇稳居榜首,七大服务商精准赋能自贸港企业 - 江湖评测
  • 2026年3月郑州黄金回收店推荐排行榜单:五大机构对比评测与选择指南 - 品牌推荐
  • 收藏!大模型 Agent 项目面试实战:如何讲好 PaiFlow 故事?亮点与难点全解析
  • 激光雷达“线”越多,自动驾驶能力就越强?一场关于感知“像素”的深度辨析
  • 计算机毕业设计springboot在线音乐服务系统 Spring Boot框架下的云端音乐流媒体播放平台开发 基于Java Web技术的智能音乐分享与社区互动系统构建
  • 计算机毕业设计springboot中华汉字学习平台 基于SpringBoot的汉字文化教育传播平台 SpringBoot架构下的传统汉字数字化学习系统
  • 《服务器硬件基础(九)——BIOS/UEFI详解:鲲鹏920关键配置项与测试注意事项》
  • Flutter Beta 版本引入 ScrollCacheExtent ,并修复长久存在的 shrinkWrap NaN 问题
  • 前端-小米商城静态版复刻总结
  • HCIP-AI-EI Developer V2.5 第五、六章笔记
  • GEE案例分析:基于Dynamic World 数据的农用地识别活跃与休耕农田
  • java之抽象类和接口
  • 万爱通礼品卡怎么回收最划算?线上流程分享 - 团团收购物卡回收
  • Python基于卷积神经网络的学情分析系统【附源码、文档说明】
  • 一键生成以假乱真的扫描件!LookScanned:cpolar 内网穿透实验室第 786成功挑战
  • 2026年3月郑州黄金回收店推荐排行榜单:五大机构客观对比与深度评测分析 - 品牌推荐
  • 洛谷:P1554 [USACO06DEC] 梦中的统计 Dream Counting B
  • 博世 HBA 液压制动辅助系统性能规范详解
  • 把杂乱网址装进口袋!Dashlet 轻量仪表盘 : cpolar 内网穿透实验室第 757 个成功挑战
  • 不学 Python,Java 也能调大模型?15 分钟跑通第一个 AI 接口(Java 架构师的 AI 工程笔记 01)
  • Java架构设计:密码加密设计最佳实践(从入门到工业级落地)
  • 什么是原型链(Prototype Chain)?proto和prototype的关系与区别是什么?
  • 【零基础入门】Python机器视觉第五阶段:目标检测实战(YOLOv8)
  • Q312B三菱主基板
  • SpringBoot 配置文件核心用法(Properties YAML)
  • Python 全栈实战 · 第8章
  • 《QGIS快速入门与应用基础》226:添加地图框工具(布局工具栏)
  • 【Vue入门】scoped与组件通信
  • 2026年推荐防水补漏公司,江苏宋家防水服务特色大剖析 - 工业品牌热点