0Pricing
Kotlin Academy · Lección

Mutex y Semaphore para el estado compartido

Proteja el estado mutable compartido con Mutex y limite la concurrencia con Semaphore.

Mutex y Semaphore para el estado compartido es una lección gratuita de Kotlin Academy en CoddyKit. Esta es la lección 3 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Kotlin Academy, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Kotlin Academy incluye 4 lecciones en total.

El problema del estado compartido

Las corrutinas que se ejecutan en varios hilos pueden corromper el estado mutable compartido. Sin sincronización, los incrementos simultáneos producen resultados incorrectos.

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
}

Conceptos básicos de Mutex

Mutex es un bloqueo de exclusión mutua para corrutinas. Suspende (no bloquea) al código que llama hasta que el bloqueo está 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
}

Extensión withLock

mutex.withLock { ... } adquiere el bloqueo, ejecuta el bloque y lo libera, incluso si se produce una excepción o una cancelación.

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 manualmente

Puede llamar directamente a lock() y unlock() para obtener un control más preciso, pero prefiera withLock para no olvidar liberar el bloqueo.

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 no es reentrante

El Mutex de Kotlin NO es reentrante. Volver a llamar a lock() desde la misma corrutina provocará un interbloqueo. Utilice un contador o refactorice el código para evitar bloqueos anidados.

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

Conceptos básicos de Semaphore

Un Semaphore(n) permite que hasta n corrutinas avancen simultáneamente. Es como un sistema de tickets con n plazas.

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

Extensión withPermit

semaphore.withPermit { ... } adquiere un permiso, ejecuta el bloque y lo libera; es el equivalente seguro para corrutinas de intentar adquirir un permiso de un semáforo.

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

Limitar la frecuencia con Semaphore

Utilice un semáforo para limitar el número de llamadas simultáneas a una API: restrínjalas a N solicitudes a la vez.

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 frente a Semaphore

Mutex: semáforo binario (1 permiso), para acceso exclusivo. Semaphore(n): n permisos, para una simultaneidad limitada. Use Mutex para proteger un recurso y Semaphore para limitar el paralelismo.

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

Contador atómico con Mutex

Implemente una clase de contador segura para subprocesos utilizando Mutex para proteger los incrementos y las lecturas.

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

Alternativa: dispatcher de un solo hilo

En lugar de un mutex, confine el estado mutable a un dispatcher de un solo hilo. Solo una corrutina se ejecuta cada vez en ese dispatcher, lo que hace que el acceso sea seguro de forma inherente.

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

Comprobación rápida

¿Cuál es la diferencia principal entre Mutex y Semaphore?

Resumen

Mutex proporciona acceso exclusivo al estado compartido; prefiera withLock. Semaphore(n) limita la simultaneidad a n. Ambos suspenden (no bloquean) las corrutinas que esperan. Para casos sencillos, un dispatcher de un solo hilo suele ser más claro.

Preguntas frecuentes

¿La lección «Mutex y Semaphore para el estado compartido» es gratis?

Sí — el texto completo de «Mutex y Semaphore para el estado compartido» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Kotlin Academy, actualiza a CoddyKit PRO. El curso de Kotlin Academy incluye 4 lecciones en total.

¿Qué aprenderé en «Mutex y Semaphore para el estado compartido»?

Proteja el estado mutable compartido con Mutex y limite la concurrencia con Semaphore. Practicas Kotlin Academy con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar Kotlin Academy?

No se requiere experiencia previa. Kotlin Academy en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 3 de 4.

¿Cuánto tiempo toma la lección «Mutex y Semaphore para el estado compartido»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de Kotlin Academy?

Sí. Cada lección de Kotlin Academy incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Fundamentos de Channel: send, receive y close
  2. Tipos de Channel: Rendezvous, Buffered, Conflated y Unlimited
  3. Mutex y Semaphore para el estado compartido
  4. Actores y gestión estructurada del estado
← Volver a Kotlin Academy