Kotlin Academy · Leçon

Mutex et Semaphore pour l’état partagé

Protégez l’état mutable partagé avec Mutex et limitez la concurrence avec Semaphore.

Leçon 3 sur 413 étapes

Mutex et Semaphore pour l’état partagé est une leçon Kotlin Academy gratuite sur CoddyKit. Ceci est la leçon 3 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Kotlin Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Kotlin Academy comprend 4 leçons au total.

Le problème de l’état partagé

Les coroutines exécutées sur plusieurs threads peuvent corrompre un état mutable partagé. Sans synchronisation, les incréments concurrents produisent des résultats incorrects.

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
}

Principes de base de Mutex

Mutex est un verrou d’exclusion mutuelle pour les coroutines. Il suspend l’appelant, sans le bloquer, jusqu’à ce que le verrou soit disponible.

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
}

Extension withLock

mutex.withLock { ... } acquiert le verrou, exécute le bloc, puis le libère, même en cas d’exception ou d’annulation.

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 manuellement

Vous pouvez appeler directement lock() et unlock() pour un contrôle plus précis, mais préférez withLock afin de ne pas oublier de déverrouiller.

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 n’est pas réentrant

Le Mutex de Kotlin n’est pas réentrant. Appeler de nouveau lock() depuis la même coroutine provoquera un interblocage. Utilisez un compteur ou remaniez le code pour éviter les verrouillages imbriqués.

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

Principes de base des sémaphores

Un Semaphore(n) permet à au plus n coroutines de progresser simultanément. C’est comparable à un système de tickets comportant n places.

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

Extension withPermit

semaphore.withPermit { ... } acquiert un permis, exécute le bloc, puis le libère : c’est l’équivalent sûr pour les coroutines d’une tentative d’acquisition d’un sémaphore.

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

Limiter le débit avec un sémaphore

Utilisez un sémaphore pour limiter le nombre d’appels d’API simultanés : limitez-les à N requêtes à la fois.

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 ou sémaphore

Mutex : sémaphore binaire (1 permis), pour un accès exclusif. Semaphore(n) : n permis, pour une concurrence limitée. Utilisez un mutex pour protéger une ressource et un sémaphore pour limiter le parallélisme.

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

Compteur atomique avec Mutex

Implémentez une classe de compteur sûre pour les threads en utilisant Mutex afin de protéger les incréments et les lectures.

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

Alternatives : répartiteur à thread unique

Au lieu d’utiliser un mutex, confinez l’état mutable à un répartiteur à thread unique. Une seule coroutine s’exécute à la fois sur ce répartiteur, ce qui rend l’accès intrinsèquement sûr.

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

Vérification rapide

Quelle est la principale différence entre Mutex et Semaphore ?

Récapitulatif

Mutex fournit un accès exclusif à l’état partagé : préférez withLock. Semaphore(n) limite la concurrence à n coroutines. Tous deux suspendent les coroutines en attente, sans les bloquer. Pour les cas simples, un répartiteur à thread unique est souvent plus clair.

Gratuit pour commencer

Apprends Kotlin avec un tuteur IA — gratuit

Écris et exécute du vrai code dans ton navigateur, obtiens de l'aide instantanée d'un tuteur IA disponible 24h/24, et reprends là où tu t'es arrêté sur le web ou dans l'app.

Cours
51
Leçons
203

Questions Fréquemment Posées

La leçon « Mutex et Semaphore pour l’état partagé » est-elle gratuite ?

Oui — le texte complet de « Mutex et Semaphore pour l’état partagé » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Kotlin Academy, passe à CoddyKit PRO. Le cours Kotlin Academy comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Mutex et Semaphore pour l’état partagé » ?

Protégez l’état mutable partagé avec Mutex et limitez la concurrence avec Semaphore. Tu pratiques Kotlin Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer Kotlin Academy ?

Aucune expérience préalable n'est requise. Kotlin Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 3 sur 4.

Combien de temps prend la leçon « Mutex et Semaphore pour l’état partagé » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon Kotlin Academy ?

Oui. Chaque leçon Kotlin Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. Notions de base des canaux : send, receive et close
  2. Types de canaux : rendez-vous, avec tampon, fusionnés, illimités
  3. Mutex et Semaphore pour l’état partagé
  4. Acteurs et gestion structurée de l’état
← Retour à Kotlin Academy