Los hilos virtuales (Project Loom) son hilos ligeros administrados por la JVM que se multiplexan sobre un pool reducido de hilos plataforma. Permiten manejar muchas conexiones I/O-bound con código bloqueante y legible, sin pagar el costo de miles de hilos nativos.
synchronized, APIs heredadas).Thread.ofVirtual().start(runnable) crea y lanza un hilo virtual.Executors.newVirtualThreadPerTaskExecutor() genera un executor que crea un hilo virtual por tarea.import java.util.concurrent.Executors;
import java.util.concurrent.ExecutorService;
public class HilosVirtualesIntro {
public static void main(String[] args) throws Exception {
Thread.ofVirtual().start(() -> System.out.println("Hola desde hilo virtual"));
try (ExecutorService exec = Executors.newVirtualThreadPerTaskExecutor()) {
exec.submit(() -> {
Thread.sleep(200);
System.out.println("Tarea virtual: " + Thread.currentThread());
return null;
}).get();
}
}
}
El factory Thread.ofVirtual() produce hilos ligeros al vuelo. El executor per-task crea un hilo virtual por cada submit y se cierra automáticamente en el try-with-resources.
Socket.read() o Thread.sleep(), el hilo virtual se aparca; el carrier queda libre.synchronized: funciona igual; pero un bloqueo largo frena el hilo virtual y todo el carrier, evitando aparcar.jcmd (p.ej. jcmd <pid> Thread.print -format=json) o banderas de trazas de Loom (-Djdk.tracePinnedThreads=full) para detectar pinning y ver dónde se fijan los hilos virtuales.import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpServer;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ServidorVirtual {
public static void main(String[] args) throws Exception {
HttpServer server = HttpServer.create(new InetSocketAddress(8080), 0);
ExecutorService exec = Executors.newVirtualThreadPerTaskExecutor();
server.createContext("/saludo", new SaludoHandler());
server.setExecutor(exec); // un hilo virtual por request
server.start();
System.out.println("Escuchando en http://localhost:8080/saludo");
}
static class SaludoHandler implements HttpHandler {
public void handle(HttpExchange exchange) throws IOException {
String body = "Hola desde un hilo virtual: " + Thread.currentThread().toString();
byte[] data = body.getBytes(StandardCharsets.UTF_8);
exchange.sendResponseHeaders(200, data.length);
try (var os = exchange.getResponseBody()) {
os.write(data);
}
}
}
}
El servidor usa un executor de hilos virtuales: cada request obtiene su propio hilo ligero, y las esperas de I/O (lectura/escritura) aparcan sin retener carriers. Perfecto para muchas conexiones concurrentes sin tunear pools.