Introducción: ¿por qué se necesitan colas de tareas en Kotlin?
\nLas aplicaciones modernas requieren cada vez más procesamiento paralelo de datos, operaciones en segundo plano y llamadas asíncronas. Sin una gestión adecuada de las tareas, su código puede enfrentar problemas de rendimiento, fugas de memoria o "congelaciones" de la interfaz. Las colas de tareas en Kotlin no son solo una abstracción, sino una herramienta poderosa que ayuda a organizar la ejecución del trabajo en el orden correcto, con control sobre la cantidad de hilos y prioridades.
\nEn este artículo, analizaremos enfoques prácticos para implementar colas de tareas en Kotlin: desde el clásico ThreadPoolExecutor hasta las corrutinas con sus flexibles despachadores. Verá cómo aplicar estas técnicas en proyectos reales y obtendrá ejemplos de código listos para usar.
1. Enfoque clásico: ThreadPoolExecutor y BlockingQueue
\nLa biblioteca estándar de Java (y Kotlin, como lenguaje JVM) proporciona la potente clase ThreadPoolExecutor. Esta gestiona un grupo de hilos y utiliza una cola de bloqueo interna para almacenar las tareas en espera.
Creación de una cola con un número fijo de hilos
\nEl caso más simple es un grupo con una cantidad fija de hilos. Todas las tareas que no se pueden ejecutar de inmediato se colocan en una cola LinkedBlockingQueue.
import java.util.concurrent.Executors\nimport java.util.concurrent.TimeUnit\n\nfun main() {\n val executor = Executors.newFixedThreadPool(3)\n\n for (i in 1..10) {\n executor.submit {\n println("Tarea $i ejecutándose en el hilo ${Thread.currentThread().name}")\n Thread.sleep(1000)\n }\n }\n\n executor.shutdown()\n executor.awaitTermination(1, TimeUnit.MINUTES)\n println("Todas las tareas completadas")\n}\n\nEste código demuestra una cola básica: las tareas se envían al grupo y, si todos los hilos están ocupados, esperan en la cola.
\n\nCola con prioridad: PriorityBlockingQueue
\nA veces es necesario ejecutar tareas urgentes antes que las demás. Para ello, usamos PriorityBlockingQueue con un comparador personalizado.
import java.util.concurrent.PriorityBlockingQueue\nimport java.util.concurrent.ThreadPoolExecutor\nimport java.util.concurrent.TimeUnit\n\ndata class PriorityTask(val priority: Int, val name: String) : Comparable<PriorityTask> {\n override fun compareTo(other: PriorityTask): Int = other.priority - this.priority\n}\n\nfun main() {\n val queue = PriorityBlockingQueue<Runnable>(10) { r1, r2 ->\n val t1 = (r1 as PriorityTaskWrapper).task\n val t2 = (r2 as PriorityTaskWrapper).task\n t1.compareTo(t2)\n }\n\n val executor = ThreadPoolExecutor(2, 4, 1, TimeUnit.SECONDS, queue)\n\n class PriorityTaskWrapper(val task: PriorityTask, val action: () -> Unit) : Runnable {\n override fun run() = action()\n }\n\n for (i in 1..5) {\n val priority = if (i % 2 == 0) 10 else 1\n executor.submit(PriorityTaskWrapper(PriorityTask(priority, "Tarea $i")) {\n println("Ejecutando: ${PriorityTask(priority, "Tarea $i")}")\n Thread.sleep(500)\n })\n }\n\n executor.shutdown()\n executor.awaitTermination(1, TimeUnit.MINUTES)\n}\n\nAquí, las tareas con priority = 10 se ejecutarán antes que las de priority = 1, incluso si se agregaron después.
2. Colas de tareas con corrutinas (kotlinx.coroutines)
\nEl Kotlin moderno ofrece corrutinas: hilos ligeros que permiten construir colas asíncronas sin gestionar hilos directamente. La herramienta principal es Channel y Dispatchers.
Cola simple mediante Channel
\nChannel es un análogo de una cola de bloqueo, pero para corrutinas. Permite transferir tareas de forma segura entre corrutinas.
import kotlinx.coroutines.*\nimport kotlinx.coroutines.channels.Channel\n\nsuspend fun processTas