0Pricing
Kotlin Academy · Lektion

Mutex und Semaphore für gemeinsam genutzten Zustand

Schützen Sie gemeinsam genutzten veränderlichen Zustand mit Mutex und begrenzen Sie die Nebenläufigkeit mit Semaphore.

Mutex und Semaphore für gemeinsam genutzten Zustand ist eine kostenlose Kotlin Academy-Lektion auf CoddyKit. Dies ist Lektion 3 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des Kotlin Academy-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der Kotlin Academy-Kurs umfasst insgesamt 4 Lektionen.

Das Problem mit gemeinsamem Zustand

Coroutines auf mehreren Threads können gemeinsam genutzten veränderlichen Zustand beschädigen. Ohne Synchronisierung führen gleichzeitige Inkrementierungen zu falschen Ergebnissen.

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-Grundlagen

Mutex ist eine Sperre zum gegenseitigen Ausschluss für Coroutines. Sie suspendiert den Aufrufer, anstatt ihn zu blockieren, bis die Sperre verfügbar ist.

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
}

Die withLock-Erweiterung

mutex.withLock { ... } erwirbt die Sperre, führt den Block aus und gibt sie wieder frei — auch bei einer Ausnahme oder Abbruch.

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 manuell

Sie können lock() und unlock() für eine genauere Steuerung direkt aufrufen. Bevorzugen Sie jedoch withLock, damit Sie das Entsperren nicht vergessen.

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 ist nicht wiedereintrittsfähig

Kotlin's Mutex ist NICHT wiedereintrittsfähig. Wenn dieselbe Coroutine erneut lock() aufruft, entsteht ein Deadlock. Verwenden Sie einen Zähler oder strukturieren Sie den Code um, um verschachtelte Sperren zu vermeiden.

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-Grundlagen

Eine Semaphore(n) ermöglicht es bis zu n Coroutines, gleichzeitig fortzufahren. Sie funktioniert wie ein Ticketsystem mit n Plätzen.

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() }
}

Die withPermit-Erweiterung

semaphore.withPermit { ... } erwirbt ein Permit, führt den Block aus und gibt es wieder frei — das coroutine-sichere Äquivalent zu einem Try-Acquire einer Semaphore.

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)
}

Ratenbegrenzung mit Semaphore

Verwenden Sie eine Semaphore, um gleichzeitige API-Aufrufe zu begrenzen — lassen Sie höchstens N Anfragen gleichzeitig zu.

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 im Vergleich zu Semaphore

Mutex: binäre Semaphore (1 Permit) für exklusiven Zugriff. Semaphore(n): n Permits für begrenzte Nebenläufigkeit. Verwenden Sie Mutex zum Schutz einer Ressource und Semaphore zur Begrenzung der Parallelität.

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") }

Atomarer Zähler mit Mutex

Implementieren Sie eine threadsichere Zählerklasse, die Mutex zum Schutz von Inkrementierungen und Lesezugriffen verwendet.

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()}")
}

Alternativen: Single-Threaded-Dispatcher

Beschränken Sie den veränderlichen Zustand anstelle eines Mutex auf einen Single-Threaded-Dispatcher. Auf diesem Dispatcher läuft immer nur eine Coroutine gleichzeitig, wodurch der Zugriff grundsätzlich sicher ist.

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()
}

Kurzer Test

Was ist der wichtigste Unterschied zwischen Mutex und Semaphore?

Zusammenfassung

Mutex gewährt exklusiven Zugriff auf gemeinsam genutzten Zustand — bevorzugen Sie withLock. Semaphore(n) begrenzt die Nebenläufigkeit auf n. Beide suspendieren wartende Coroutines, anstatt sie zu blockieren. Für einfache Fälle ist ein Single-Threaded-Dispatcher oft übersichtlicher.

Häufig gestellte Fragen

Ist die Lektion „Mutex und Semaphore für gemeinsam genutzten Zustand“ kostenlos?

Ja — der vollständige Text von „Mutex und Semaphore für gemeinsam genutzten Zustand“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des Kotlin Academy-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der Kotlin Academy-Kurs umfasst insgesamt 4 Lektionen.

Was lerne ich in „Mutex und Semaphore für gemeinsam genutzten Zustand“?

Schützen Sie gemeinsam genutzten veränderlichen Zustand mit Mutex und begrenzen Sie die Nebenläufigkeit mit Semaphore. Du übst Kotlin Academy mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.

Brauche ich Erfahrung, um Kotlin Academy zu starten?

Keine Vorkenntnisse erforderlich. Kotlin Academy auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 3 von 4.

Wie lange dauert die Lektion „Mutex und Semaphore für gemeinsam genutzten Zustand“?

Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.

Kann ich in dieser Kotlin Academy-Lektion Code schreiben und ausführen?

Ja. Jede Kotlin Academy-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.

Alle Lektionen in diesem Kurs

  1. Channel-Grundlagen: send, receive und close
  2. Channel-Typen: Rendezvous, Buffered, Conflated, Unlimited
  3. Mutex und Semaphore für gemeinsam genutzten Zustand
  4. Actors und strukturierte Zustandsverwaltung
← Zurück zu Kotlin Academy