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
- Fundamentos de Channel: send, receive y close
- Tipos de Channel: Rendezvous, Buffered, Conflated y Unlimited
- Mutex y Semaphore para el estado compartido
- Actores y gestión estructurada del estado