Se você já sofreu com isso aqui:
Thread pool travando
Timeout em API
Sistema engasgando com muitas requisições
então você entende o problema.
Java sempre teve concorrência forte… mas pesada.
Agora com Virtual Threads: o jogo mudou de verdade.
1. O problema das threads tradicionais
Modelo clássico:
ExecutorService executor = Executors.newFixedThreadPool(10);
for (int i = 0; i < 1000; i++) {
executor.submit(() -> {
callExternalApi();
});
}Problema:
só 10 threads rodando
resto fica esperando
gargalo absurdo
2. O custo de uma thread tradicional
Thread padrão:
~1MB de memória
gerenciada pelo SO
troca de contexto cara
Agora imagina: 10 mil requisições simultâneas. Já era.
3. Virtual Threads (Project Loom)
Agora olha isso:
ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
for (int i = 0; i < 1000; i++) {
executor.submit(() -> {
callExternalApi();
});
}Simples assim.
Sem pool. Sem limite artificial.
4. O que muda de verdade
Virtual Thread:
leve (quase zero memória)
gerenciada pela JVM
milhões de threads possíveis
Parece async… Mas você escreve código síncrono.
5. Comparação direta
Thread tradicional:
Thread thread = new Thread(() -> process());
thread.start();Virtual Thread:
Thread.startVirtualThread(() -> process());Mesma ideia. Só que MUITO mais eficiente.
6. Exemplo real (API concorrente)
Simulando chamadas externas:
public void processRequests() throws InterruptedException {
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
List<Future<String>> futures = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
futures.add(executor.submit(() -> {
Thread.sleep(100); // simula I/O
return "OK";
}));
}
for (var future : futures) {
future.get();
}
}
}1000 requisições concorrentes sem travar
7. Benchmark real (simulação)
Cenário:
1000 tarefas
cada uma com delay de 100ms
Thread Pool (10 threads)
Tempo aproximado:
~10.000 ms (10 segundos)Execução em lotes gargalo claro
Virtual Threads
Tempo aproximado:
~100 - 200 msExecução quase simultânea ganho absurdo
8. Quando usar (e quando NÃO usar)
Use Virtual Threads para:
chamadas de API
banco de dados
I/O (arquivos, rede)
microserviços
NÃO use para:
tarefas CPU-heavy
processamento intensivo
loops pesados
Aqui o ganho é zero.
9. Exemplo com Spring Boot
Controller simples:
@GetMapping("/test")
public String test() throws Exception {
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
var future1 = executor.submit(this::callServiceA);
var future2 = executor.submit(this::callServiceB);
return future1.get() + future2.get();
}
}Execução paralela limpa sem CompletableFuture complexo
10. Substituindo código antigo
ANTES:
CompletableFuture<String> f1 = CompletableFuture.supplyAsync(this::callA);
CompletableFuture<String> f2 = CompletableFuture.supplyAsync(this::callB);
return f1.get() + f2.get();DEPOIS:
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
var f1 = executor.submit(this::callA);
var f2 = executor.submit(this::callB);
return f1.get() + f2.get();
}Mais simples mais legível menos bug
11. Conclusão
Virtual Threads não é melhoria pequena.
É mudança estrutural no Java.
Você ganha:
escalabilidade
simplicidade
performance
Sem mudar o modelo mental.
