Skip to content

并发控制:Mutex 与 Semaphore

在协程世界中,传统的 synchronizedReentrantLock 会阻塞线程,严禁使用。Kotlin 提供了非阻塞的替代方案。

互斥锁:Mutex

Mutex (Mutual Exclusion) 是异步的锁。

核心特性

  • 非阻塞: 当获取不到锁时,协程挂起(释放线程),等待锁释放后恢复。
  • 不可重入 (Non-reentrant): 这是与 synchronized 最大的不同。同一个协程不能连续两次加锁,否则会死锁。
kotlin
val mutex = Mutex()

suspend fun safeUpdate() {
    // 自动加锁,执行完自动释放 (即使抛异常)
    mutex.withLock {
        // 临界区逻辑
        sharedData++
    }
}

死锁陷阱

kotlin
mutex.withLock {
    // ❌ 错误:Mutex 不可重入!这里会永久挂起。
    mutex.withLock { ... } 
}

信号量:Semaphore

用于限制同时访问某些资源(如数据库连接、网络请求)的并发数量。

kotlin
val semaphore = Semaphore(3) // 允许 3 个并发

repeat(100) {
    launch {
        semaphore.withPermit {
            // 同一时刻最多只有 3 个协程在运行此块代码
            download()
        }
    }
}

替代 Actor:Channel 状态机

早期的 actor 构建器已被废弃。现在推荐使用 Channel 来实现无锁的并发状态管理(CSP 模型)。

原理:将状态限制在单个协程内,通过 Channel 接收命令来修改状态。

kotlin
sealed class Msg
class Add(val value: Int) : Msg()
class Get(val resp: CompletableDeferred<Int>) : Msg()

fun CoroutineScope.counterActor() = launch {
    var counter = 0 // 状态被封闭在局部变量,天然线程安全
    val channel = Channel<Msg>()
    
    // 消费逻辑
    for (msg in channel) {
        when (msg) {
            is Add -> counter += msg.value
            is Get -> msg.resp.complete(counter)
        }
    }
}