Skip to content

09. Kotlin 协程详解

目录

  1. 协程基础概念
  2. 协程 vs 传统线程
  3. CoroutineScope 作用域
  4. Job 生命周期管理
  5. Dispatchers 调度器
  6. launch vs async 对比
  7. 协程取消机制
  8. 异常处理
  9. 协程调度器自定义
  10. 结构化并发
  11. 协程泄漏检测
  12. 性能优化
  13. 面试考点

1. 协程基础概念

1.1 什么是协程

协程(Coroutine) 是 Kotlin 提供的一种轻量级线程,用于简化异步编程。协程允许以同步的代码风格编写异步代码,通过挂起和恢复机制实现非阻塞操作。

kotlin
// 协程的基本概念
import kotlinx.coroutines.*

suspend fun myCoroutine() {
    // 这是一个挂起函数
    delay(1000)  // 非阻塞延迟
    println("Coroutine executed")
}

fun main() = runBlocking {
    // runBlocking 是一个协程构建器
    myCoroutine()
}

1.2 协程的核心组件

kotlin
// 协程三要素:
// 1. Scope (作用域) - 协程的生命周期容器
// 2. Job (作业) - 协程的执行任务
// 3. Dispatcher (调度器) - 协程运行的线程池

coroutineScope {
    // Scope 定义协程的作用域
    launch(Dispatchers.Default) {  // Dispatcher 指定调度器
        // Job 自动创建并管理
        delay(1000)
        println("Work done")
    }
}

1.3 挂起函数(suspend)

kotlin
// 挂起函数:可以挂起而不阻塞线程的函数
suspend fun fetchUserData(): User {
    // 挂起操作,不会阻塞线程
    delay(1000)
    return User("Alice", 30)
}

// 只能在协程或其他挂起函数中调用
fun main() = runBlocking {
    val user = fetchUserData()  // ✅ 正确
}

// ❌ 错误:不能在普通函数中调用挂起函数
// fun regularFunction() {
//     fetchUserData()  // 编译错误
// }

1.4 协程构建器

kotlin
// 主要协程构建器
coroutineScope {
    // 轻量级作用域,等待所有子协程完成
}

supervisorScope {
    // 异常隔离的作用域
}

withContext(Dispatchers.IO) {
    // 切换上下文执行
}

runBlocking {
    // 阻塞当前线程直到协程完成
}

2. 协程 vs 传统线程

2.1 资源对比

特性线程协程
内存占用1MB+KB 级别
创建开销极低
切换开销高(内核态)低(用户态)
并发能力百级别百万级别
管理复杂度

2.2 代码对比

kotlin
// 传统线程方式
fun traditionalAsync() {
    Thread {
        println("Working in thread")
        Thread.sleep(1000)
        println("Done")
    }.start()
}

// 协程方式
suspend fun coroutineAsync() {
    println("Working in coroutine")
    delay(1000)  // 非阻塞
    println("Done")
}

// 多线程 vs 多协程
fun traditionalMultiThread() {
    val threads = listOf(1, 2, 3, 4, 5).map { i ->
        Thread {
            Thread.sleep(1000)
            println("Thread $i done")
        }
    }
    threads.forEach { it.start() }
    threads.forEach { it.join() }
}

suspend fun coroutineMultiCoroutine() {
    listOf(1, 2, 3, 4, 5).forEach { i ->
        launch {
            delay(1000)
            println("Coroutine $i done")
        }
    }
}

2.3 阻塞 vs 非阻塞

kotlin
// 传统线程 - 阻塞
fun blockingOperation() {
    println("Start")
    Thread.sleep(1000)  // 阻塞线程 1 秒
    println("End")
}

// 协程 - 非阻塞
suspend fun nonBlockingOperation() {
    println("Start")
    delay(1000)  // 挂起,不阻塞线程
    println("End")
}

fun main() = runBlocking {
    println("Before")
    nonBlockingOperation()
    println("After")  // 立即执行,不等待 1 秒
}

2.4 性能对比测试

kotlin
fun benchmarkThreads() {
    val iterations = 10000
    val start = System.currentTimeMillis()
    
    repeat(iterations) {
        Thread {
            Thread.sleep(1)
        }.start()
    }
    
    val end = System.currentTimeMillis()
    println("Threads: ${end - start} ms")
}

suspend fun benchmarkCoroutines() {
    val iterations = 10000
    val start = System.currentTimeMillis()
    
    repeat(iterations) {
        launch {
            delay(1)
        }
    }
    
    val end = System.currentTimeMillis()
    println("Coroutines: ${end - start} ms")
}

// 典型结果:
// Threads: 100+ ms(线程创建开销大)
// Coroutines: 10+ ms(协程创建开销小)

3. CoroutineScope 作用域

3.1 作用域的基本概念

kotlin
// CoroutineScope 是协程的容器
interface CoroutineScope {
    val coroutineContext: CoroutineContext
}

// 作用域包含:
// 1. Job - 生命周期管理
// 2. Dispatcher - 线程调度
// 3. ExceptionHandler - 异常处理

3.2 创建作用域

kotlin
// 1. 临时作用域
coroutineScope {
    // 等待所有子协程完成
    launch { /* ... */ }
    launch { /* ... */ }
}  // 等待完成后才继续

// 2. 结构化作用域
supervisorScope {
    // 异常不会传播到父协程
    launch { /* ... */ }
}

// 3. 长期作用域
val scope = CoroutineScope(SupervisorJob() + Dispatchers.Default)

3.3 作用域的生命周期

kotlin
class ViewModel : ViewModel() {
    // ViewModel 的作用域,随 ViewModel 销毁
    val viewModelScope = CoroutineScope(SupervisorJob())
    
    init {
        viewModelScope.launch {
            // 协程在 ViewModel 销毁时自动取消
            loadData()
        }
    }
    
    override fun onCleared() {
        super.onCleared()
        viewModelScope.cancel()  // 取消所有协程
    }
}

3.4 作用域传播

kotlin
// 父作用域的上下文会传播到子协程
coroutineScope {
    launch {
        // 继承父作用域的 Job 和 Dispatcher
        println(coroutineContext)
    }
}

// 可以覆盖继承的上下文
coroutineScope(Dispatchers.IO) {
    launch(Dispatchers.Main) {  // 覆盖为 Main
        println(coroutineContext)
    }
}

3.5 作用域取消传播

kotlin
// 父作用域取消,所有子协程也会取消
val scope = CoroutineScope(Job())

scope.launch {
    launch {
        // 子协程
        println("Child running")
    }
}

// 取消父作用域
scope.cancel()
// 所有子协程都会被取消

4. Job 生命周期管理

4.1 Job 状态

kotlin
// Job 的状态
enum class JobState {
    NEW,         // 新建
    CREATED,     // 已创建
    ACTIVE,      // 活跃
    COMPLETING,  // 完成中
    COMPLETED,   // 已完成
    CANCELLED,   // 已取消
    CANCELLING,  // 取消中
    FAILED       // 失败
}

// 状态检查
val job = launch {
    // ...
}

println(job.isActive)   // true - 是否活跃
println(job.isCompleted) // false - 是否完成
println(job.isCancelled) // false - 是否取消
println(job.isCancelledOrCompleted) // false

4.2 Job 等待

kotlin
// 等待协程完成
val job = launch {
    delay(1000)
    println("Done")
}

job.join()  // 阻塞等待完成
// 或使用
job.await()  // 挂起等待

// 非阻塞等待
job.invokeOnCompletion {
    println("Job completed: $it")
}

4.3 Job 取消

kotlin
// 取消协程
val job = launch {
    repeat(1000) { i ->
        delay(100)
        println("Processing: $i")
    }
}

delay(500)
job.cancel()  // 取消协程

// 取消并清理
job.cancelAndJoin()

// 取消原因
job.cancel(CancellationException("User cancelled"))

4.4 Job 层次结构

kotlin
// 父子关系
val parent = launch {
    val child1 = launch {
        // 子协程 1
    }
    val child2 = launch {
        // 子协程 2
    }
    
    // 等待子协程
    child1.join()
    child2.join()
}

// 取消父协程,子协程也会被取消
parent.cancel()

4.5 Job 监控

kotlin
class JobMonitor {
    private val jobs = mutableListOf<Job>()
    
    fun track(job: Job) {
        jobs.add(job)
        job.invokeOnCompletion { exception ->
            jobs.remove(job)
            if (exception != null) {
                println("Job failed: $exception")
            }
        }
    }
    
    fun cancelAll() {
        jobs.forEach { it.cancel() }
        jobs.clear()
    }
    
    fun activeCount(): Int = jobs.count { it.isActive }
}

5. Dispatchers 调度器

5.1 内置调度器

kotlin
// Dispatchers.Main - 主线程
launch(Dispatchers.Main) {
    // UI 更新操作
    textView.text = "Hello"
}

// Dispatchers.Default - 默认调度器(计算密集型)
launch(Dispatchers.Default) {
    // 计算密集型任务
    val result = heavyComputation()
}

// Dispatchers.IO - IO 密集型
launch(Dispatchers.IO) {
    // 网络请求、文件读写
    val data = readFile()
}

// Dispatchers.Unconfined - 无限制
launch(Dispatchers.Unconfined) {
    // 不限制线程,不推荐在 Android 中使用
}

5.2 调度器对比

kotlin
// Dispatchers.Default
// - 使用 CPU 核心数的线程池
// - 适用于计算密集型任务
// - 默认调度器

// Dispatchers.IO
// - 使用大量线程(通常 64 或设备核心数 * 32)
// - 适用于 IO 密集型任务
// - 线程可以阻塞

// Dispatchers.Main
// - 单线程(主线程)
// - 适用于 UI 更新
// - Android 中自动配置

// Dispatchers.Unconfined
// - 不限定线程
// - 在哪个线程开始就在哪个线程继续
// - 不推荐使用(可能导致线程混乱)

5.3 切换上下文

kotlin
// 在协程中切换调度器
suspend fun loadData() = withContext(Dispatchers.IO) {
    // IO 操作
    val data = api.fetchData()
    
    withContext(Dispatchers.Default) {
        // 计算操作
        data.process()
    }
    
    withContext(Dispatchers.Main) {
        // UI 更新
        updateUI(data)
    }
}

// 使用 launch 指定调度器
launch(Dispatchers.IO) {
    val data = fetchData()
    withContext(Dispatchers.Main) {
        updateUI(data)
    }
}

5.4 调度器性能

kotlin
// Default 调度器 - CPU 核心数线程
fun testDefaultDispatcher() = runBlocking {
    repeat(100) { i ->
        launch(Dispatchers.Default) {
            delay(10)
            println("Default: $i")
        }
    }
}

// IO 调度器 - 大量线程
fun testIODispatcher() = runBlocking {
    repeat(1000) { i ->
        launch(Dispatchers.IO) {
            delay(10)
            println("IO: $i")
        }
    }
}

// Main 调度器 - 单线程
fun testMainDispatcher() = runBlocking {
    repeat(10) { i ->
        launch(Dispatchers.Main) {
            println("Main: $i")
        }
    }
}

5.5 调度器顺序

kotlin
// Default -> Main 的顺序优化
suspend fun fetchDataAndShow(): User {
    return withContext(Dispatchers.Default) {
        // 先切换到 Default 进行计算
        val user = api.fetchUser()
        processUser(user)
    }
    // 自动切换回原来的上下文(通常是 Main)
}

// 显式顺序
suspend fun explicitOrder(): User {
    return withContext(Dispatchers.IO) {
        withContext(Dispatchers.Default) {
            withContext(Dispatchers.Main) {
                // 最后在主线程执行
            }
        }
    }
}

6. launch vs async 对比

6.1 核心区别

kotlin
// launch - -fire and forget(不返回值)
val job = launch {
    delay(1000)
    println("Launched")
}

// async - 返回值(Deferred<T>)
val deferred = async {
    delay(1000)
    "Async result"
}
val result = deferred.await()

6.2 使用场景

kotlin
// launch 场景:不需要结果
launch {
    // 日志记录
    logEvent("User clicked")
    
    // 推送通知
    sendNotification("New message")
    
    // 更新数据库
    saveToDatabase(data)
}

// async 场景:需要结果
val deferred1 = async {
    fetchUser()
}

val deferred2 = async {
    fetchPosts()
}

val user = deferred1.await()
val posts = deferred2.await()

6.3 并行执行

kotlin
// 并行执行多个异步任务
suspend fun parallelExecution() {
    val deferred1 = async { fetchUser() }
    val deferred2 = async { fetchPosts() }
    val deferred3 = async { fetchComments() }
    
    // 等待所有任务完成
    val user = deferred1.await()
    val posts = deferred2.await()
    val comments = deferred3.await()
    
    // 组合结果
    showUserWithPostsAndComments(user, posts, comments)
}

// 使用 awaitAll
suspend fun parallelWithAwaitAll() {
    val deferreds = listOf(
        async { fetchUser() },
        async { fetchPosts() },
        async { fetchComments() }
    )
    
    val results = awaitAll(*deferreds.toTypedArray())
}

6.4 错误处理差异

kotlin
// launch 的错误会传播到父协程
launch {
    throw Exception("Launch error")  // 父协程会捕获
}

// async 的错误延迟到 await 时抛出
val deferred = async {
    throw Exception("Async error")  // 在 await 时抛出
}

try {
    deferred.await()
} catch (e: Exception) {
    println("Caught: ${e.message}")
}

6.5 性能对比

kotlin
// launch 性能(无返回值开销)
suspend fun benchmarkLaunch() {
    val jobs = listOf(1..1000).map { i ->
        launch {
            delay(1)
        }
    }
    jobs.forEach { it.join() }
}

// async 性能(有返回值开销)
suspend fun benchmarkAsync() {
    val deferreds = listOf(1..1000).map { i ->
        async {
            delay(1)
            i
        }
    }
    deferreds.forEach { it.await() }
}

// async 比 launch 慢约 10-20%(由于返回值包装)

6.6 结构化并发

kotlin
// launch 和 async 都支持结构化并发
coroutineScope {
    val job1 = launch { /* ... */ }
    val job2 = launch { /* ... */ }
    // 等待所有子协程完成
}

coroutineScope {
    val deferred1 = async { /* ... */ }
    val deferred2 = async { /* ... */ }
    // 等待所有子协程完成
    val result1 = deferred1.await()
    val result2 = deferred2.await()
}

7. 协程取消机制

7.1 取消原理

kotlin
// 协程取消通过抛异常实现
suspend fun cancellableOperation() {
    repeat(1000) { i ->
        // 检查取消
        ensureActive()  // 如果已取消,抛出 CancellationException
        
        delay(100)  // delay 内部会检查取消
        
        println("Processing: $i")
    }
}

// delay 的取消检查
suspend fun delay(ms: Long) {
    // 内部实现会定期检查 isCancelled
    // 如果取消,抛出 CancellationException
}

7.2 取消传播

kotlin
// 父协程取消,子协程也会取消
val parent = CoroutineScope(Job())

parent.launch {
    launch {
        // 子协程 1
    }
    launch {
        // 子协程 2
    }
}

parent.cancel()  // 所有子协程都会被取消

7.3 可取消操作

kotlin
// 可取消的协程操作
suspend fun cancellableFetch() {
    withContext(Dispatchers.IO) {
        // IO 操作可以被取消
        val data = api.fetchData()
    }
}

// 不可取消的操作(需要特殊处理)
suspend fun nonCancellableFetch() {
    // 普通线程操作不会自动取消
    Thread {
        Thread.sleep(1000)  // 不会响应协程取消
    }.join()
}

// 可取消的线程操作
suspend fun cancellableThreadOperation() {
    val job = Job()
    val thread = Thread {
        // 线程工作
    }
    
    try {
        withContext(job) {
            thread.join()
        }
    } finally {
        thread.interrupt()  // 中断线程
    }
}

7.4 取消处理

kotlin
// 捕获取消异常
suspend fun handleCancellation() {
    try {
        launch {
            delay(1000)
        }.join()
    } catch (e: CancellationException) {
        println("Cancelled: ${e.message}")
    }
}

// 忽略取消
suspend fun ignoreCancellation() {
    try {
        // 操作
    } catch (e: CancellationException) {
        // 忽略
    }
}

// 重新抛出取消
suspend fun rethrowCancellation() {
    try {
        // 操作
    } catch (e: CancellationException) {
        throw e  // 重新抛出
    }
}

7.5 取消最佳实践

kotlin
// 最佳实践 1:使用 cancelAndJoin
val job = launch {
    // ...
}

job.cancelAndJoin()  // 取消并等待完成

// 最佳实践 2:使用 withTimeout
suspend fun withTimeoutExample() {
    withTimeout(1000) {
        // 1 秒内完成,否则取消
        longRunningOperation()
    }
}

// 最佳实践 3:使用 SupervisorJob 隔离取消
supervisorScope {
    launch {
        // 失败不会取消其他协程
    }
    launch {
        // 失败不会取消其他协程
    }
}

8. 异常处理

8.1 异常传播

kotlin
// launch 的异常传播到父协程
launch {
    throw Exception("Error")  // 父协程会捕获
}

// async 的异常延迟到 await
val deferred = async {
    throw Exception("Error")  // await 时抛出
}

try {
    deferred.await()
} catch (e: Exception) {
    println("Caught: ${e.message}")
}

8.2 SupervisorJob 异常隔离

kotlin
// 使用 SupervisorJob 隔离异常
val scope = CoroutineScope(SupervisorJob())

scope.launch {
    try {
        throw Exception("Child 1 error")
    } catch (e: Exception) {
        println("Child 1: ${e.message}")
    }
}

scope.launch {
    // 不会受到 Child 1 异常影响
    println("Child 2 running")
}

// SupervisorJob 的异常不会传播到父协程
supervisorScope {
    launch {
        throw Exception("Error")  // 不会取消其他协程
    }
    launch {
        // 继续执行
    }
}

8.3 try-catch 处理

kotlin
// 在协程中使用 try-catch
suspend fun safeOperation() {
    try {
        riskyOperation()
    } catch (e: Exception) {
        println("Error: ${e.message}")
    }
}

// 在 launch 中处理
launch {
    try {
        riskyOperation()
    } catch (e: Exception) {
        println("Launch error: ${e.message}")
    }
}

// 在 async 中处理
val deferred = async {
    try {
        riskyOperation()
    } catch (e: Exception) {
        defaultValue
    }
}

8.4 全局异常处理

kotlin
// 设置全局异常处理器
CoroutineScope(
    SupervisorJob() + 
    Dispatchers.Default +
    CoroutineExceptionHandler { _, exception ->
        println("Unhandled exception: $exception")
    }
) {
    launch {
        throw Exception("Global error")
    }
}

// 在 Android 中
class GlobalExceptionHandler : CoroutineExceptionHandler {
    override fun handleException(context: CoroutineContext, exception: Throwable) {
        // 记录日志
        Timber.e(exception)
        
        // 显示错误
        showError(exception.message)
    }
}

8.5 异常恢复

kotlin
// 使用 retry 恢复
suspend fun <T> retry(
    times: Int = 3,
    delay: Duration = 1.second,
    block: suspend () -> T
): T {
    repeat(times) { attempt ->
        try {
            return block()
        } catch (e: Exception) {
            if (attempt == times - 1) throw e
            delay(delay)
        }
    }
    throw IllegalStateException("Should not reach here")
}

// 使用
suspend fun fetchWithRetry() {
    val data = retry(times = 3) {
        api.fetchData()
    }
}

9. 协程调度器自定义

9.1 创建自定义调度器

kotlin
// 使用 Executors 创建
val customDispatcher = Executors.newFixedThreadPool(10).asCoroutineDispatcher()

// 使用 CoroutineDispatcher
val myDispatcher = MyCoroutineDispatcher()

class MyCoroutineDispatcher : CoroutineDispatcher() {
    override fun dispatch(context: CoroutineContext, block: Runnable) {
        // 自定义调度逻辑
        threadPool.execute(block)
    }
    
    override fun isDispatchNeeded(context: CoroutineContext): Boolean {
        // 是否需要切换上下文
        return !isSameThread()
    }
}

9.2 调度器参数

kotlin
// 有限制的调度器
val limitedDispatcher = Dispatchers.Default.limitedParallelism(4)

// 带延迟的调度器
val delayedDispatcher = Dispatchers.Default.limitedParallelism(
    parallelism = 4,
    capacity = 100,
    onCapacityExceeded = { 
        println("Capacity exceeded") 
    }
)

9.3 调度器关闭

kotlin
// 关闭自定义调度器
val dispatcher = Executors.newFixedThreadPool(4).asCoroutineDispatcher()

coroutineScope(dispatcher) {
    launch {
        // 使用自定义调度器
    }
}

dispatcher.close()  // 关闭调度器

9.4 调度器调试

kotlin
// 调度器名称
val namedDispatcher = newFixedThreadPool(4, 
    ThreadFactory { runnable ->
        Thread(runnable, "My-Dispatcher-${threadCount.getAndIncrement()}")
    }
).asCoroutineDispatcher()

// 调度器监控
class DispatcherMonitor {
    fun monitor(dispatcher: CoroutineDispatcher) {
        // 监控调度器状态
    }
}

10. 结构化并发

10.1 结构化并发原理

kotlin
// 结构化并发:协程作为构建块
coroutineScope {
    // 所有子协程都是这个作用域的一部分
    // 父作用域等待所有子协程完成
    launch { /* ... */ }
    launch { /* ... */ }
}  // 等待所有子协程完成

10.2 结构化并发示例

kotlin
// 典型的结构化并发场景
suspend fun fetchUserData(): UserData {
    return coroutineScope {
        val userDeferred = async { fetchUser() }
        val postsDeferred = async { fetchPosts() }
        val commentsDeferred = async { fetchComments() }
        
        UserData(
            user = userDeferred.await(),
            posts = postsDeferred.await(),
            comments = commentsDeferred.await()
        )
    }
}

10.3 取消传播

kotlin
// 结构化并发的取消传播
coroutineScope {
    val job1 = launch {
        delay(1000)
        println("Job 1")
    }
    
    val job2 = launch {
        delay(2000)
        println("Job 2")
    }
    
    // 取消作用域
    cancel()
    
    // job1 和 job2 都会被取消
}

10.4 错误隔离

kotlin
// 使用 supervisorScope 错误隔离
supervisorScope {
    launch {
        throw Exception("Job 1 error")
    }
    
    launch {
        // 继续执行,不受 Job 1 错误影响
        println("Job 2 running")
    }
}

11. 协程泄漏检测

11.1 泄漏原因

kotlin
// 泄漏原因 1:未等待协程完成
fun leakyFunction() {
    launch {
        // 协程在函数返回后继续运行
    }
    // 函数返回,协程可能泄漏
}

// 泄漏原因 2:持有协程引用
class LeakyClass {
    val jobs = mutableListOf<Job>()
    
    fun addJob() {
        val job = launch { /* ... */ }
        jobs.add(job)  // 持有引用,无法 GC
    }
}

11.2 检测方法

kotlin
// 使用 Android Profiler 检测
// 1. 内存分析
// 2. 线程分析
// 3. CPU 分析

// 代码检测
fun checkLeakedCoroutines() {
    val activeJobs = coroutineContext[Job]?.children?.toList()
    if (activeJobs != null) {
        println("Active jobs: ${activeJobs.size}")
    }
}

11.3 预防泄漏

kotlin
// 预防 1:使用结构化并发
coroutineScope {
    // 自动等待所有子协程
}

// 预防 2:及时取消
val job = launch { /* ... */ }
// 在不需要时取消
job.cancel()

// 预防 3:使用 viewModelScope
class ViewModel : ViewModel() {
    // viewModelScope 在 ViewModel 销毁时自动取消
    viewModelScope.launch {
        // 安全
    }
}

11.4 泄漏调试

kotlin
// 添加调试日志
class DebugCoroutineScope(private val scope: CoroutineScope) {
    fun launch(block: suspend CoroutineScope.() -> Unit): Job {
        val job = scope.launch {
            println("Coroutine started: ${coroutineContext}")
            try {
                block()
            } finally {
                println("Coroutine finished: ${coroutineContext}")
            }
        }
        return job
    }
}

12. 性能优化

12.1 减少协程创建

kotlin
// 优化 1:复用协程
val sharedScope = CoroutineScope(Dispatchers.Default)

// 优化 2:批量处理
suspend fun batchProcess(items: List<Item>) {
    items.chunked(100).forEach { batch ->
        batch.forEach { item ->
            process(item)
        }
    }
}

12.2 调度器优化

kotlin
// 使用合适的调度器
suspend fun optimizedOperation() {
    withContext(Dispatchers.IO) {
        // IO 操作
    }
    withContext(Dispatchers.Default) {
        // 计算操作
    }
}

12.3 内存优化

kotlin
// 避免持有协程引用
class OptimizedClass {
    private val scope = CoroutineScope(Dispatchers.Default)
    
    fun process() {
        scope.launch {
            // 不保存 job 引用
        }
    }
}

12.4 并发控制

kotlin
// 使用 Semaphore 控制并发
val semaphore = Semaphore(10)

suspend fun controlledConcurrent(items: List<Item>) {
    items.forEach { item ->
        withSemaphore(semaphore) {
            process(item)
        }
    }
}

13. 面试考点

13.1 基础考点

Q1: 什么是协程?它与线程的区别?

A:

  • 协程是轻量级的线程,在用户态运行
  • 线程在内核态,协程在用户态
  • 协程创建开销小,线程创建开销大
  • 协程切换开销小,线程切换开销大

Q2: 协程的三大组件?

A:

  • Scope(作用域)- 协程的生命周期容器
  • Job(作业)- 协程的执行任务
  • Dispatcher(调度器)- 协程运行的线程

Q3: suspend 函数的作用?

A:

  • 标记函数可以挂起
  • 只能在协程或其他挂起函数中调用
  • 不阻塞线程,只是暂停执行

13.2 进阶考点

Q4: launch 和 async 的区别?

A:

  • launch 返回 Job,不返回值
  • async 返回 Deferred,需要 await 获取结果
  • launch 用于 fire-and-forget
  • async 用于需要结果的场景

Q5: 结构化并发的原理?

A:

  • 父协程等待所有子协程完成
  • 子协程的异常会传播到父协程
  • 取消父协程会取消所有子协程

13.3 高级考点

Q6: 协程取消机制的实现?

A:

  • 通过抛 CancellationException 实现
  • delay 等挂起函数会检查取消状态
  • 确保协程可以安全地取消

Q7: 如何实现协程泄漏检测?

A:

  • 使用 Android Profiler
  • 检查未完成的 Job
  • 使用 DebugCoroutineScope 添加日志

Q8: 协程性能优化策略?

A:

  • 减少协程创建
  • 使用合适的调度器
  • 避免持有协程引用
  • 使用批量处理

总结

Kotlin 协程是异步编程的革命性工具,通过轻量级线程和挂起恢复机制,提供了简洁高效的并发编程方式。掌握协程的核心概念、生命周期管理、异常处理和性能优化,是成为高级 Android 开发者的必备技能。

核心要点:

  1. 轻量级 - 协程比线程更轻量
  2. 非阻塞 - delay 不阻塞线程
  3. 结构化并发 - 清晰的协程层次
  4. 异常处理 - SupervisorJob 隔离异常
  5. 性能优化 - 合理使用调度器