Spring Boot y Virtual Threads
¿Una alternativa real a los servicios de mensajería en arquitecturas monolíticas?
La llegada de Java 21 y la oficialización de Project Loom marcaron un antes y un después en cómo la JVM maneja la concurrencia. Durante años, escalar aplicaciones con operaciones bloqueantes de entrada/salida (I/O) implicaba un compromiso costoso: o aumentabas drásticamente los recursos de hardware para soportar miles de Platform Threads, o adoptabas modelos de programación reactiva que añadían complejidad cognitiva al código.
Con la introducción de los Virtual Threads (hilos virtuales), Spring Boot ofrece ahora una herramienta poderosa que promete el rendimiento de lo asíncrono con la simplicidad de lo secuencial. Pero, ¿hasta dónde llega su potencia? ¿Es posible que esta tecnología haga innecesarios los servicios de mensajería tradicionales (como RabbitMQ o Kafka) en ciertos contextos?
En este artículo analizamos técnicamente cómo funcionan, cómo implementarlos en Spring Boot y el análisis costo-beneficio de utilizarlos como reemplazo de colas de mensajes en arquitecturas monolíticas.
¿Qué son realmente los Virtual Threads?
A diferencia de los hilos de plataforma tradicionales, que son costosos y tienen una relación 1:1 con los hilos del sistema operativo, los Virtual Threads son entidades ligeras gestionadas directamente por la JVM.
La diferencia en consumo de recursos es abismal: mientras un hilo de plataforma puede consumir megabytes de memoria para su pila, un hilo virtual consume apenas cientos de bytes. Esto permite un cambio de paradigma fundamental:
El modelo «Under the Hood»
La magia reside en cómo la JVM maneja las operaciones bloqueantes. Cuando un hilo virtual realiza una llamada a una base de datos o una API externa (I/O), la JVM lo «desmonta» de su hilo portador (carrier thread), liberando ese recurso del sistema operativo para que procese otras tareas. Cuando la operación de I/O termina, el hilo virtual se «remonta» y continúa su ejecución.
Esto revive el estilo de programación «thread-per-request». Podés escribir código secuencial, fácil de leer y depurar, obteniendo la escalabilidad que antes solo ofrecía la programación reactiva.
Nota importante: Los Virtual Threads no hacen que el código sea más rápido en tareas intensivas de CPU. Su valor está en la escalabilidad (throughput) para tareas que pasan mucho tiempo esperando (I/O bound).
Virtual Threads vs. Servicios de Mensajería
En arquitecturas de microservicios distribuidos, los servicios de mensajería (RabbitMQ, Kafka, SQS) son innegociables para garantizar el desacoplamiento. Sin embargo, en aplicaciones monolíticas o servicios autocontenidos con procesos por etapas, solemos usar colas internas simplemente para paralelizar tareas.
Aquí es donde los Virtual Threads plantean una alternativa interesante: Reemplazar la complejidad de un broker de mensajería con un patrón de Pipeline interno en memoria.
Análisis crítico: ¿Cuándo conviene hacer el cambio?
Reemplazar un sistema de mensajería robusto por hilos en memoria conlleva riesgos que debés evaluar. A continuación, comparamos ambas aproximaciones para que tomes una decisión informada.

El veredicto técnico
Si optás por Virtual Threads para reemplazar colas, perdés la persistencia automática y los reintentos nativos. Esto significa que, si el sistema se cae a mitad de un proceso crítico, perdés el estado de esa tarea a menos que implementes tu propio mecanismo de persistencia en base de datos.
Recomendamos usar Virtual Threads como reemplazo de colas solo si:
- El proceso es simple, secuencial y está bien definido.
- La pérdida de un mensaje ocasional no es crítica para el negocio.
- Buscás reducir latencia eliminando intermediarios.
- Tenés implementado un mecanismo de recuperación (Scheduler) que escanee la base de datos buscando procesos «atascados» para reintentarlos, como en el caso de uso de reprocesamiento de Leads.
La solución híbrida: Lo mejor de los dos mundos
Para sistemas de misión crítica, la respuesta no suele ser «uno u otro», sino ambos.
Podés mantener RabbitMQ como columna vertebral para la comunicación entre servicios (garantizando durabilidad y desacoplamiento) y utilizar Virtual Threads dentro de los consumidores.
Por ejemplo, en un servicio de procesamiento de Leads, el consumidor de RabbitMQ recibe el mensaje y delega el trabajo pesado (transcripción, detección, validación) a un VirtualThreadExecutor. Esto maximiza la concurrencia del consumidor, permitiéndole procesar miles de mensajes en paralelo sin bloquear hilos del sistema operativo esperando respuestas de APIs externas, mientras mantienes la seguridad de la cola.