0Pricing
Kotlin Academy · 课时

使用 Mutex 与 Semaphore 管理共享状态

使用 Mutex 保护共享可变状态,并使用 Semaphore 限制并发。

使用 Mutex 与 Semaphore 管理共享状态 是 CoddyKit 上的免费 Kotlin Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 Kotlin Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 Kotlin Academy 课程共包含 4 节课。

共享状态问题

在多个线程上运行的协程可能会破坏共享的可变状态。如果没有同步,并发递增会产生错误的结果。

import kotlinx.coroutines.*
fun main() = runBlocking {
    var counter = 0
    val jobs = List(1000) {
        launch(Dispatchers.Default) { counter++ } // race condition!
    }
    jobs.forEach { it.join() }
    println("Expected 1000, got: $counter") // likely wrong
}

Mutex 基础

Mutex 是用于协程的互斥锁。它会挂起(而不是阻塞)调用方,直到锁可用。

import kotlinx.coroutines.*
import kotlinx.coroutines.sync.*
fun main() = runBlocking {
    val mutex = Mutex()
    var counter = 0
    val jobs = List(1000) {
        launch(Dispatchers.Default) {
            mutex.withLock { counter++ }  // safe
        }
    }
    jobs.forEach { it.join() }
    println("Counter: $counter") // 1000
}

withLock 扩展

mutex.withLock { ... } 会获取锁、运行代码块,然后释放锁——即使发生异常或取消也能如此。

import kotlinx.coroutines.*
import kotlinx.coroutines.sync.*
fun main() = runBlocking {
    val mutex = Mutex()
    var list = mutableListOf<Int>()
    List(10) {
        launch {
            mutex.withLock {
                list.add(it)
            }
        }
    }.forEach { it.join() }
    println(list.sorted())
}

手动使用 lock / unlock

您可以直接调用 lock() 和 unlock() 进行更细致的控制,但请优先使用 withLock,以避免忘记解锁。

import kotlinx.coroutines.*
import kotlinx.coroutines.sync.*
fun main() = runBlocking {
    val mutex = Mutex()
    mutex.lock()
    try {
        println("Critical section")
    } finally {
        mutex.unlock()  // must always release
    }
}

Mutex 不可重入

Kotlin 的 Mutex 不可重入。在同一个协程中再次调用 lock() 会导致死锁。请使用计数器或重构代码,以避免嵌套加锁。

import kotlinx.coroutines.*
import kotlinx.coroutines.sync.*
fun main() = runBlocking {
    val mutex = Mutex()
    // This deadlocks:
    // mutex.withLock { mutex.withLock { println("never") } }
    mutex.withLock {
        println("Acquired once — safe")
    }
}

Semaphore 基础

Semaphore(n) 允许最多 n 个协程并发执行。它就像一个拥有 n 个名额的票证系统。

import kotlinx.coroutines.*
import kotlinx.coroutines.sync.*
fun main() = runBlocking {
    val semaphore = Semaphore(3) // max 3 concurrent
    List(10) { i ->
        launch {
            semaphore.withPermit {
                println("Concurrent task $i"); delay(100)
            }
        }
    }.forEach { it.join() }
}

withPermit 扩展

semaphore.withPermit { ... } 会获取许可、运行代码块并释放许可——这是信号量尝试获取许可的协程安全等价方式。

import kotlinx.coroutines.*
import kotlinx.coroutines.sync.*
fun main() = runBlocking {
    val sem = Semaphore(2)
    val results = mutableListOf<String>()
    List(5) { i ->
        launch {
            sem.withPermit {
                delay(50)
                synchronized(results) { results.add("task-$i") }
            }
        }
    }.forEach { it.join() }
    println(results)
}

使用 Semaphore 限制速率

使用信号量限制并发接口调用的速率——将同时进行的请求数限制为 N 个。

import kotlinx.coroutines.*
import kotlinx.coroutines.sync.*
val apiSemaphore = Semaphore(5) // max 5 concurrent requests
suspend fun callApi(id: Int): String {
    return apiSemaphore.withPermit {
        delay(100) // simulate network
        "Response-$id"
    }
}
fun main() = runBlocking {
    val results = (1..20).map { async { callApi(it) } }.awaitAll()
    println("Got ${results.size} responses")
}

Mutex 与 Semaphore

Mutex:二进制信号量(1 个许可),用于独占访问。Semaphore(n):具有 n 个许可,用于限制并发量。使用 Mutex 保护资源,使用 Semaphore 限制并行度。

import kotlinx.coroutines.sync.*
// Mutex = Semaphore(1) for exclusive access
// Semaphore(n) = n-way concurrency limit
val exclusive = Mutex()          // one at a time
val limited   = Semaphore(3)     // three at a time
fun main() { println("Mutex for exclusion; Semaphore for rate limiting") }

使用 Mutex 实现原子计数器

使用 Mutex 保护递增和读取操作,实现一个线程安全的计数器类。

import kotlinx.coroutines.*
import kotlinx.coroutines.sync.*
class AtomicCounter {
    private val mutex = Mutex()
    private var count = 0
    suspend fun increment() = mutex.withLock { count++ }
    suspend fun get() = mutex.withLock { count }
}
fun main() = runBlocking {
    val counter = AtomicCounter()
    List(100) { launch { counter.increment() } }.forEach { it.join() }
    println("Count: ${counter.get()}")
}

替代方案:单线程调度器

您可以不使用互斥锁,而是将可变状态限制在单线程调度器中。该调度器一次只运行一个协程,因此访问天然安全。

import kotlinx.coroutines.*
fun main() = runBlocking {
    val singleThread = newSingleThreadContext("CounterThread")
    var counter = 0
    List(1000) {
        launch(singleThread) { counter++ }
    }.forEach { it.join() }
    println("Counter: $counter") // 1000, no mutex needed
    singleThread.close()
}

快速检查

Mutex 和 Semaphore 的主要区别是什么?

回顾

Mutex 为共享状态提供独占访问——请优先使用 withLock。Semaphore(n) 将并发量限制为 n。两者都会挂起(而不是阻塞)等待中的协程。对于简单场景,单线程调度器通常更简洁。

常见问题解答

「使用 Mutex 与 Semaphore 管理共享状态」课时是免费的吗?

是的 — 「使用 Mutex 与 Semaphore 管理共享状态」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 Kotlin Academy 课程的其余内容,请升级到 CoddyKit PRO。 Kotlin Academy 课程共包含 4 节课。

「使用 Mutex 与 Semaphore 管理共享状态」这节课中我会学到什么?

使用 Mutex 保护共享可变状态,并使用 Semaphore 限制并发。 你通过在浏览器中直接运行的动手代码来练习 Kotlin Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。

学习 Kotlin Academy 需要有经验吗?

无需任何先前经验。CoddyKit 上的 Kotlin Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。

「使用 Mutex 与 Semaphore 管理共享状态」课时需要多长时间?

大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。

我能在这节 Kotlin Academy 课中编写并运行代码吗?

能。每节 Kotlin Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。

此课程中的所有课时

  1. Channel 基础:send、receive 与 close
  2. Channel 类型:会合、缓冲、合并与无限
  3. 使用 Mutex 与 Semaphore 管理共享状态
  4. Actor 与结构化状态管理
← 返回 Kotlin Academy