Concurrencia y multihilo en Kotlin: nivel avanzado

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Concurrencia y multihilo en Kotlin: nivel avanzado



En el desarrollo moderno, la gestión eficiente de la concurrencia es clave para crear aplicaciones rápidas y receptivas. Kotlin, gracias al soporte integrado de corrutinas (coroutines), ofrece un conjunto de herramientas potente y flexible que va mucho más allá de los hilos estándar de Java. En este artículo, exploraremos conceptos avanzados de concurrencia y multihilo en Kotlin, incluyendo el trabajo con Channel, Flow, Mutex y los detalles de los planificadores.



1. Corrutinas: más allá de lo básico



Las corrutinas son hilos ligeros que permiten escribir código asíncrono en un estilo imperativo. Sin embargo, a nivel avanzado es importante entender cómo funcionan los despachadores (Dispatchers) y los contextos de las corrutinas.



Planificadores (Dispatchers)

Elegir el despachador correcto es crítico para el rendimiento:

  • Dispatchers.Default — para tareas intensivas en CPU (por defecto usa un pool de hilos igual al número de núcleos del procesador).
  • Dispatchers.IO — para operaciones de entrada/salida (red, archivos). El pool de hilos puede expandirse hasta 64 hilos (o más, según la configuración).
  • Dispatchers.Main — para el hilo de UI (Android, JavaFX).
  • newSingleThreadContext y newFixedThreadPoolContext — para crear pools propios.


import kotlinx.coroutines.*

fun main() = runBlocking { launch(Dispatchers.Default) { println("Tarea intensiva en CPU en ${Thread.currentThread().name}") } launch(Dispatchers.IO) { println("Tarea de E/S en ${Thread.currentThread().name}") }}


Concurrencia estructurada

Una de las principales características de Kotlin es la concurrencia estructurada. Garantiza que las corrutinas hijas finalicen antes de que finalice la corrutina padre. Esto previene fugas de memoria y simplifica la gestión del ciclo de vida.



fun main() = runBlocking {    coroutineScope {        launch {            delay(1000)            println("Tarea 1 completada")        }        launch {            delay(500)            println("Tarea 2 completada")        }    }    println("Todas las tareas completadas")}


2. Canales (Channel) para transferir datos entre corrutinas



Channel es un análogo de una cola bloqueante (BlockingQueue), pero para corrutinas. Permite transferir datos de forma segura entre corrutinas, suspendiendo al emisor si el canal está lleno, o al receptor si el canal está vacío.



Tipos de canales

  • RendezvousChannel (por defecto) — no tiene búfer. El envío se bloquea hasta que se recibe.
  • BufferedChannel — tiene un búfer de tamaño definido.
  • ConflatedChannel — almacena solo el último valor enviado, sobrescribiendo los anteriores.
  • UnlimitedChannel — búfer ilimitado (cuidado: puede causar OOM).


import kotlinx.coroutines.*import kotlinx.coroutines.channels.*

fun main() = runBlocking { val channel = Channel<Int>(capacity = 5) // Canal con búfer

launch { for (i in 1..10) { channel.send(i) println("Enviado: $i") } channel.close() }

launch { for (value in channel) { println("Recibido: $value") delay(100) } }}


Fan-out y Fan-in

Los canales son ideales para los patrones "fan-out" (un productor, varios consumidores) y "fan-in" (varios productores, un consumidor).



3. Flow: flujos de datos reactivos



A diferencia de Channel, que es "caliente" (los datos se envían independientemente de si hay un suscriptor), Flow es un flujo "frío" que

Blogs

Book Recommendations