Kotlin中的并发与多线程:从入门到精通
现代应用程序越来越需要同时执行多个任务。在Kotlin中,与其他JVM语言一样,多线程传统上通过Thread类实现。然而,该语言提供了一种更强大、更优雅的工具——协程(coroutines)。在本文中,我们将深入探讨Kotlin中并发与多线程的各个方面:从基本概念到高级技术。
1. 多线程基础:Thread和Runnable
我们从经典方法开始。在Kotlin中,与Java一样,可以直接创建线程:
fun main() { val thread = Thread { for (i in 1..5) { println("线程: $i") Thread.sleep(500) } } thread.start() println("主线程已完成")}然而,手动管理线程存在缺点:
- 高开销——创建和销毁线程代价高昂。
- 同步复杂——容易导致竞态条件。
- 线程数量有限——系统只能创建有限数量的线程。
为了同步对共享数据的访问,使用关键字synchronized或锁ReentrantLock:
class Counter { private var count = 0 @Synchronized fun increment() { count++ }}2. 协程——现代的并发方法
协程是轻量级线程,解决了经典多线程的问题。它们不绑定到系统线程,可以在单个线程上执行,在任务之间切换。
2.1. 第一个协程
要使用协程,需要添加依赖org.jetbrains.kotlinx:kotlinx-coroutines-core。最简单的协程示例:
import kotlinx.coroutines.*
fun main() = runBlocking { launch { delay(1000L) println("世界!") } println("你好,")}这里runBlocking是普通代码和协程之间的桥梁。launch启动一个新的协程,而delay是Thread.sleep()的替代品,但不会阻塞线程。
2.2. 挂起函数
关键字suspend允许在不阻塞线程的情况下暂停函数的执行:
suspend fun fetchData(): String { delay(2000L) // 模拟网络请求 return "数据已获取"}
fun main() = runBlocking { val result = async { fetchData() } println("等待中...") println(result.await())}2.3. 结构化并发
Kotlin支持结构化并发——所有子协程在父协程完成之前自动完成:
fun main() = runBlocking { coroutineScope { launch { delay(1000L) println("任务 1") } launch { delay(500L) println("任务 2") } } println("所有任务已完成")}3. 高级技术:Channel、Flow和Dispatchers
3.1. 通道(Channel)
通道允许在协程之间交换数据。它是队列的替代品:
fun main() = runBlocking { val channel = Channel<Int>() launch { for (i in 1..5) { channel.send(i * i) } channel.close() } for (value in channel) { println(value) }}3.2. Flow——冷数据流
Flow是一个响应式流,按需发射值:
fun simpleFlow(): Flow<Int> = flow { for (i in 1..3) { delay(