Concurrencia y multihilo en Kotlin: de la A a la Z

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: de la A a la Z



Las aplicaciones modernas requieren cada vez más la ejecución de múltiples tareas simultáneamente. En Kotlin, al igual que en otros lenguajes JVM, el multihilo se implementa tradicionalmente a través de la clase Thread. Sin embargo, el lenguaje ofrece una herramienta mucho más potente y elegante: las corutinas (coroutines). En este artículo, analizaremos todos los aspectos de la concurrencia y el multihilo en Kotlin: desde conceptos básicos hasta técnicas avanzadas.



1. Fundamentos del multihilo: Thread y Runnable



Empecemos con el enfoque clásico. En Kotlin, al igual que en Java, se puede crear un hilo directamente:



fun main() {    val thread = Thread {        for (i in 1..5) {            println("Hilo: $i")            Thread.sleep(500)        }    }    thread.start()    println("Hilo principal finalizado")}


Sin embargo, la gestión manual de hilos tiene desventajas:

  • Alto costo general — crear y destruir hilos es costoso.
  • Complejidad de sincronización — es fácil caer en una condición de carrera.
  • Número limitado de hilos — el sistema solo puede crear un número limitado de hilos.


Para sincronizar el acceso a datos compartidos se utiliza la palabra clave synchronized o los bloqueos ReentrantLock:



class Counter {    private var count = 0    @Synchronized    fun increment() {        count++    }}


2. Corutinas: el enfoque moderno para la concurrencia



Las corutinas son hilos ligeros que resuelven los problemas del multihilo clásico. No están vinculadas a hilos del sistema y pueden ejecutarse en un solo hilo, alternando entre tareas.



2.1. Primera corutina



Para trabajar con corutinas, es necesario agregar la dependencia org.jetbrains.kotlinx:kotlinx-coroutines-core. Ejemplo de una corutina simple:



import kotlinx.coroutines.*

fun main() = runBlocking { launch { delay(1000L) println("¡Mundo!") } println("Hola,")}


Aquí, runBlocking es un puente entre el código normal y las corutinas. launch inicia una nueva corutina y delay es un análogo de Thread.sleep(), pero sin bloquear el hilo.



2.2. Funciones suspend



La palabra clave suspend permite pausar la ejecución de una función sin bloquear el hilo:



suspend fun fetchData(): String {    delay(2000L) // simula una solicitud de red    return "Datos recibidos"}

fun main() = runBlocking { val result = async { fetchData() } println("Esperando...") println(result.await())}


2.3. Concurrencia estructurada



Kotlin admite concurrencia estructurada: todas las corutinas hijas se completan automáticamente antes de que finalice la corutina padre:



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


3. Técnicas avanzadas: Channel, Flow y Dispatchers



3.1. Canales (Channel)



Los canales permiten intercambiar datos entre corutinas. Son un análogo de una cola:



fun main() = runBlocking {    val channel = Channel<Int>()        launch {        for (i in 1..5) {            channel.send(i * i)        }        channel.close()    }        for (value in channel) {        println(value)    }}


3.2. Flow: flujos de datos fríos



Flow es un flujo reactivo que emite valores bajo demanda:



fun simpleFlow(): Flow<Int> = flow {    for (i in 1..3) {        delay(                

Blogs

Book Recommendations