Kotlin中的并发与多线程:进阶篇
在现代开发中,高效的并发管理是创建快速响应式应用程序的关键。Kotlin凭借对协程(coroutines)的内置支持,提供了远超标准Java线程的强大而灵活的工具集。本文将探讨Kotlin中并发与多线程的高级概念,包括Channel、Flow、Mutex的使用以及调度器的细微差别。
1. 协程:超越基础
协程是轻量级线程,允许以命令式风格编写异步代码。然而在进阶层面,理解调度器(Dispatchers)和协程上下文的工作原理至关重要。
调度器(Dispatchers)
选择合适的调度器对性能至关重要:
Dispatchers.Default— 用于CPU密集型任务(默认使用与CPU核心数相等的线程池)。Dispatchers.IO— 用于输入输出操作(网络、文件)。线程池可扩展至64个线程(或更多,取决于配置)。Dispatchers.Main— 用于UI线程(Android、JavaFX)。newSingleThreadContext和newFixedThreadPoolContext— 用于创建自定义线程池。
import kotlinx.coroutines.*
fun main() = runBlocking { launch(Dispatchers.Default) { println("CPU-bound task on ${Thread.currentThread().name}") } launch(Dispatchers.IO) { println("IO-bound task on ${Thread.currentThread().name}") }}结构化并发
Kotlin的主要特性之一是结构化并发。它确保子协程在父协程完成之前完成。这可以防止内存泄漏并简化生命周期管理。
fun main() = runBlocking { coroutineScope { launch { delay(1000) println("Task 1 completed") } launch { delay(500) println("Task 2 completed") } } println("All tasks completed")}2. 通道(Channel):协程间数据传输
Channel是阻塞队列(BlockingQueue)的协程对应物。它允许在协程之间安全地传输数据,当通道满时挂起发送方,当通道空时挂起接收方。
通道类型
- RendezvousChannel(默认)— 无缓冲区。发送会阻塞直到被接收。
- BufferedChannel — 具有指定大小的缓冲区。
- ConflatedChannel — 仅存储最后发送的值,覆盖之前的值。
- UnlimitedChannel — 无限制缓冲区(注意:可能导致OOM)。
import kotlinx.coroutines.*import kotlinx.coroutines.channels.*
fun main() = runBlocking { val channel = Channel<Int>(capacity = 5) // Buffered channel
launch { for (i in 1..10) { channel.send(i) println("Sent: $i") } channel.close() }
launch { for (value in channel) { println("Received: $value") delay(100) } }}扇出与扇入
通道非常适合"扇出"(一个生产者,多个消费者)和"扇入"(多个生产者,一个消费者)模式。
3. Flow:响应式数据流
与"热"数据流(无论是否有订阅者都会发送数据)的Channel不同,Flow是一种"冷"数据流,它