Java3 min de leitura

Virtual Threads - Pinning e Saturação

Java 21: Não deixe o "Pinning" sabotar a performance das suas Virtual Threads

Virtual Threads - Pinning

Com o lançamento do Java 21, a comunidade de desenvolvedores recebeu as Virtual Threads, uma funcionalidade revolucionária que promete um throughput massivo e alta escalabilidade para aplicações modernas. Contudo, existe um comportamento técnico conhecido como Pinning que pode anular esses ganhos se não for tratado corretamente.


Faça os teste locais: Virtual-Threads - Pinning - E se você gostou, me deixe uma estrelinha ⭐


O que são Virtual Threads?

Diferente das threads de plataforma tradicionais, as Virtual Threads são entidades leves gerenciadas pela JVM e armazenadas na Heap. O grande diferencial de performance está no processo de Mounting (montagem) e Unmounting (desmontagem): quando uma thread virtual encontra uma operação de bloqueio, a JVM a remove da thread real da CPU (carrier thread), liberando esse recurso para processar outras tarefas simultâneas.


O Problema: O que é o Pinning?

O Pinning (estacionamento) ocorre quando uma thread virtual fica "presa" à sua carrier thread e não pode ser desmontada durante um bloqueio. Quando isso acontece, tanto a thread virtual quanto a thread do sistema operacional subjacente ficam bloqueadas, impedindo a escalabilidade massiva prometida pela tecnologia.

Os dois principais cenários que causam pinning atualmente são:

  1. Execução de código dentro de um bloco ou método synchronized.
  2. Invocação de funções estrangeiras ou métodos nativos (via JNI ou a nova Foreign Function & Memory API).

Por que o synchronized é perigoso?

Se um bloco synchronized contiver uma operação de bloqueio (como I/O ou Thread.sleep()), a thread virtual ficará pinned à thread portadora. Em testes práticos, uma carga de 25 tarefas executadas em 8 núcleos de CPU pode ocupar todos os 8 trabalhadores do sistema simultaneamente, criando um gargalo desnecessário e anulando a vantagem das threads leves.


A Solução: ReentrantLock

A solução recomendada para o Java moderno é refatorar os blocos sincronizados, substituindo-os por travas da API java.util.concurrent, especificamente o ReentrantLock. Diferente do synchronized, o ReentrantLock permite que a JVM realize o unmounting da thread virtual com sucesso durante o bloqueio, permitindo que a CPU seja reutilizada para outras milhares de threads virtuais que aguardam processamento.


Exemplo Prático de Refatoração

Código com Pinning (Vulnerável):

&lt;span class=<span class="hljs-string">&quot;hljs-comment&quot;</span>&gt;<span class="hljs-comment">// Causa PINNING e esgota os workers da CPU&lt;/span&gt;</span>
&lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">synchronized</span>&lt;/span&gt; (lockObject) {
    &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">try</span>&lt;/span&gt; {
        Thread.sleep(&lt;span class=<span class="hljs-string">&quot;hljs-number&quot;</span>&gt;<span class="hljs-number">1000</span>&lt;/span&gt;); &lt;span class=<span class="hljs-string">&quot;hljs-comment&quot;</span>&gt;<span class="hljs-comment">// Operação de bloqueio&lt;/span&gt;</span>
    } &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">catch</span>&lt;/span&gt; (InterruptedException e) {
        Thread.currentThread().interrupt();
    }
}

Código Escalável com ReentrantLock:

&lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">private</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">final</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-type&quot;</span>&gt;Lock&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-variable&quot;</span>&gt;lock&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-operator&quot;</span>&gt;=&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">new</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-title class_&quot;</span>&gt;ReentrantLock&lt;/span&gt;();

&lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">public</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">void</span>&lt;/span&gt; &lt;span class=<span class="hljs-string">&quot;hljs-title function_&quot;</span>&gt;executeTask&lt;/span&gt;&lt;span class=<span class="hljs-string">&quot;hljs-params&quot;</span>&gt;()&lt;/span&gt; {
    lock.lock(); &lt;span class=<span class="hljs-string">&quot;hljs-comment&quot;</span>&gt;<span class="hljs-comment">// Permite unmounting se houver bloqueio interno&lt;/span&gt;</span>
    &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">try</span>&lt;/span&gt; {
        Thread.sleep(&lt;span class=<span class="hljs-string">&quot;hljs-number&quot;</span>&gt;<span class="hljs-number">1000</span>&lt;/span&gt;); 
        logger.info(&lt;span class=<span class="hljs-string">&quot;hljs-string&quot;</span>&gt;&amp;quot;Executado com sucesso!&amp;quot;&lt;/span&gt;);
    } &lt;span class=<span class="hljs-string">&quot;hljs-keyword&quot;</span>&gt;<span class="hljs-keyword">finally</span>&lt;/span&gt; {
        lock.unlock(); &lt;span class=<span class="hljs-string">&quot;hljs-comment&quot;</span>&gt;<span class="hljs-comment">// Garantia de liberação da trava&lt;/span&gt;</span>
    }
}

Como monitorar o Pinning?

Para identificar se sua aplicação está sofrendo com este problema, você pode utilizar a flag de diagnóstico do JDK durante a execução:

-Djdk.tracePinnedThreads=short ou -Djdk.tracePinnedThreads=full.

Conclusão

As Virtual Threads mudaram o paradigma da concorrência no Java, mas exigem que abandonemos práticas antigas como o uso excessivo de synchronized em prol de soluções mais modernas como o ReentrantLock ssa mudança é essencial para atingir o máximo potencial de escalabilidade da plataforma.

Este artigo foi inspirado nos problemas resolvidos do livro Java Coding Problems, Second Edition (Packt Publishing).


Publicado por: Guilherme Gomes - 17/05/2026 20:11

← Voltar aos Artigos