¿Qué son los hilos virtuales (Project Loom) y en qué se diferencian de los hilos de SO o los green threads de runtimes más viejos?
Los hilos virtuales se ven exactamente como hilos comunes de Java, cuestan casi nada crearlos por millones, y se desmontan en silencio del hilo de SO debajo cada vez que bloquearían.
APRENDE HACIENDOPon a prueba tu intuición3 pasos
¿Qué son los hilos virtuales (Project Loom) y en qué se diferencian de los hilos de SO o los green threads de runtimes más viejos?
Usa los ejemplos y la explicación de la lección para comprobar tu idea.
Reflexión personal. No se envía a ningún servicio de IA.#El techo de escalabilidad de un-hilo-por-request
El modelo tradicional de concurrencia en la JVM mapea cada unidad de trabajo concurrente —típicamente una request HTTP— a un hilo de plataforma, un envoltorio fino sobre un hilo real del sistema operativo. Los hilos de plataforma son caros: cada uno reserva un stack de tamaño fijo (1MB por defecto, ajustable pero raramente achicable de forma segura mucho por debajo de unos cientos de KB), y el scheduler del SO hace cambios de contexto entre ellos con overhead real — transiciones de modo privilegiado, guardado de registros, contaminación de caché y TLB. En la práctica, una sola JVM empieza a sufrir en algún punto de los pocos miles de hilos vivos: presión de memoria sólo por los stacks, contención del scheduler, y throughput decreciente por cada hilo adicional. Ese techo es exactamente lo que empujó a la industria hacia la programación asíncrona y reactiva —cadenas de callbacks, pipelines de CompletableFuture, Project Reactor— no porque a los ingenieros les gustara más, sino porque le permite a un pool chico y fijo de hilos de plataforma atender muchísimas más operaciones concurrentes al nunca bloquear un hilo en I/O. El costo conocido es que el código reactivo es más difícil de escribir, debuggear, y leer en un stack trace que el código bloqueante lineal.
#Qué es realmente un hilo virtual
Project Loom, finalizado como JEP 444 en Java 21, introduce los hilos virtuales: hilos livianos implementados y planificados por la propia JVM en vez del sistema operativo. Para tu código se ven exactamente como java.lang.Thread —misma API, mismo Thread.currentThread(), mismas thread-locals, mismo try/catch alrededor de llamadas bloqueantes— pero pueden existir millones a la vez, porque el stack de cada hilo virtual vive en el heap, arranca con apenas unos cientos de bytes, y crece o se achica según haga falta. No hay un hilo de SO dedicado detrás de cada uno. En cambio, la JVM multiplexa muchos hilos virtuales sobre un pool chico de hilos portadores (carrier threads) — hilos de plataforma comunes, por defecto el ForkJoinPool común dimensionado según los cores de CPU disponibles.
El mecanismo que hace esto realmente útil, y no sólo barato de crear: cuando un hilo virtual ejecuta una llamada bloqueante que el JDK hizo consciente de los hilos virtuales —operaciones de socket bloqueantes de java.io/java.net, java.sql a través de un driver JDBC actualizado, locks de java.util.concurrent, Thread.sleep— se desmonta de su hilo portador en vez de bloquearlo. El portador queda libre de inmediato para correr otro hilo virtual. Cuando la operación bloqueante termina, el hilo virtual original se remonta, posiblemente en un portador distinto, y retoma exactamente donde había quedado. Desde el punto de vista del código no cambió nada —sigue siendo una llamada bloqueante ordinaria, lineal— pero el hilo de plataforma debajo nunca quedó ocioso.
#Por qué esto no es lo mismo que los green threads de antes
Java pre-1.3 traía "green threads": múltiples hilos livianos planificados enteramente por la JVM sobre un único hilo de SO subyacente. Eso daba concurrencia, pero nunca paralelismo real — sólo las instrucciones de un green thread podían ejecutarse en cualquier instante físico, en cualquier CPU. Peor aún, una syscall bloqueante que el runtime no supiera específicamente hacer asíncrona colgaba el único hilo de SO debajo, congelando a todos los demás green threads junto con él. Las primeras JVMs abandonaron el modelo justo por eso, pasando a hilos nativos planificados por el SO (1:1) para Java 1.3. Los hilos virtuales traen de vuelta la idea del hilo liviano pero arreglan ambos problemas a la vez: se planifican con paralelismo real sobre un pool de varios hilos portadores (planificación M:N, no M:1), y las propias APIs bloqueantes de I/O del JDK están integradas con el scheduler de hilos virtuales, así que una llamada bloqueante libera a su portador en vez de colgarlo.
| Modelo | Planificado por | Stack | Cantidad práctica máxima | I/O bloqueante |
|---|---|---|---|---|
| Hilo de plataforma | Kernel del SO | Fijo, ~1MB | Pocos miles | Bloquea el hilo de SO |
| Green thread clásico | JVM, sobre un solo hilo de SO | Fijo, más chico | Miles, pero sin paralelismo | Bloquea el único hilo de SO debajo, congelando a todos |
| Hilo virtual (Loom) | JVM, sobre N hilos portadores | En el heap, crece desde unos cientos de bytes | Millones | Desmonta al portador, retoma después |
#Código
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {List<Future<String>> futures = IntStream.range(0, 100_000).mapToObj(i -> executor.submit(() -> fetchUser(i))).toList();for (var future : futures) {String result = future.get();handle(result);}} // executor.close() bloquea hasta que terminan todas las tareas enviadas
Thread worker = Thread.ofVirtual().name("order-worker-", 0).unstarted(() -> processOrder(orderId));worker.start();worker.join();
El primer ejemplo envía 100.000 tareas, cada una haciendo una llamada bloqueante, un hilo virtual por tarea, todas respaldadas por un puñado de hilos portadores. Probá el equivalente con Executors.newFixedThreadPool(100_000) hilos de plataforma y la JVM se queda sin memoria de stack mucho antes de quedarse sin CPU.
🧠 Check your understanding
0/3 · 0/3 answered1. ¿Qué le permite específicamente a un hilo virtual evitar bloquear su hilo de SO subyacente durante I/O?
2. ¿Por qué los green threads clásicos de Java pre-1.3 no podían lograr paralelismo real?
3. ¿Qué es el "pinning" de un hilo virtual y por qué importa?