Ir para o conteúdo

Para desenvolvedores Java, o modelo “thread-per-request” sempre foi sinônimo de código simples e legível. No entanto, em um mundo de microsserviços e alta concorrência, esse modelo atinge um gargalo: as threads de plataforma, gerenciadas pelo sistema operacional, são um recurso caro e limitado. Quando uma thread bloqueia em uma operação de I/O (como uma chamada a um banco de dados ou a uma API externa), ela fica ociosa, desperdiçando recursos e limitando a capacidade do sistema de lidar com novas requisições. 

Por anos, a solução para isso foi a complexidade da programação reativa. Com o Java 21 e o Project Loom, a promessa é obter a escalabilidade do reativo com a simplicidade do código síncrono através das Virtual Threads

Mas qual o impacto real dessa mudança? Para responder a essa pergunta, conduzi um estudo de performance A/B em uma API REST para medir, com dados concretos, o ganho de performance ao habilitar as Virtual Threads. 
 
O Laboratório: criando um ambiente de teste controlado 
 
Para garantir que os resultados fossem confiáveis e reprodutíveis, o primeiro passo foi criar um ambiente de teste isolado. A aplicação sob teste é uma API REST para transações financeiras, construída com Spring Boot 3.5.5 e Java 21. 
 
O ambiente de teste foi 100% conteinerizado e com recursos controlados, composto por: 

  • Aplicação Sob Teste: Rodando em um contêiner Docker com limites de 1.5 vCPUs e 2GB de RAM. 
  • Dependência Externa Mockada: A API externa do Tesouro foi simulada com o WireMock, rodando em um contêiner separado com 0.5 vCPU e 1GB de RAM. O mock foi configurado com um delay de resposta variável (distribuição log-normal) para simular a latência de uma rede real. 
  • Gerador de Carga: O k6 foi usado para gerar o tráfego, também rodando em seu próprio contêiner com 2.0 vCPUs e 1GB de RAM. 
     

O Experimento: isolando o I/O para o Teste A/B 
 
O objetivo era criar um cenário 100% I/O-bound para estressar especificamente a capacidade do servidor de gerenciar threads bloqueadas. Para isso, o cache da aplicação foi desabilitado, informando o parâmetro “-e SPRING_CACHE_TYPE=none” no momento das execuções. 

Foram executados dois testes de estresse idênticos, de 1 minuto cada, com 50 usuários virtuais (VUs) rodando sem pausa (sleep). A única variável alterada entre os testes foi a configuração de threads do Spring Boot: 

  • Teste A (Controle): Usando threads de plataforma tradicionais (spring.threads.virtual.enabled=false). 
  • Teste B (Experimento): Usando Virtual Threads (spring.threads.virtual.enabled=true). 
     

Os resultados: a prova nos números 

Os resultados da comparação foram conclusivos e demonstraram um ganho de performance massivo. 
 

Métrica Sem Virtual Threads (Platform) Com Virtual Threads (Virtual) Melhoria 
Vazão (RPS) ~105 reqs/s ~136 reqs/s +29.5% 
Latência Média 355 ms 272 ms -23.4% 
Latência p95 794 ms 508 ms -36% 
Taxa de Erro 0.00% 0.00% (Estável) 

 
Os resultados: a prova nos números

Os números mostram que a versão com Virtual Threads processou ~30% mais requisições e foi ~36% mais rápida para 95% dos usuários. A razão para essa diferença dramática está em como os dois modelos de thread lidam com o bloqueio de I/O. 

Com Platform Threads, quando uma requisição fazia uma chamada para a API externa (mockada), a thread do servidor ficava bloqueada, ociosa, esperando a resposta. Com 50 usuários concorrentes, o pool limitado de threads do Tomcat rapidamente se esgotava, forçando novas requisições a esperar em uma fila, o que aumentava a latência. 

Com Virtual Threads, o cenário muda. Quando uma requisição bloqueava esperando pelo I/O, a thread da plataforma era imediatamente liberada para atender outra requisição. O sistema conseguiu lidar com muito mais requisições “em voo” simultaneamente, resultando em menos tempo de fila, menor latência e uma vazão muito maior. 

Conclusão: o futuro é (quase) “grátis” 

O experimento demonstra que habilitar as Virtual Threads em aplicações Spring Boot I/O-bound é um dos ganhos de performance mais impactantes e de menor esforço disponíveis no ecossistema Java moderno. 

Para serviços que passam a maior parte do tempo esperando por respostas de bancos de dados, outras APIs ou qualquer operação de rede, o Project Loom oferece um aumento de escalabilidade transformador, sem a complexidade de reescrever o código para um paradigma reativo. É, na prática, uma otimização de performance quase “gratuita”, que permite que aplicações síncronas e legíveis escalem como nunca. 

Outras publicações