Os threads virtuais do Java oferecem uma alternativa leve aos threads de sistema operacional tradicionais, permitindo um gerenciamento eficiente de simultaneidade. Mas compreender o seu comportamento é crucial para um desempenho ideal. Esta postagem do blog se aprofunda na fixação, um cenário que pode impactar a execução de threads virtuais, e explora técnicas para monitorar e resolver isso.
Os threads virtuais do Java são entidades gerenciadas que são executadas sobre os threads subjacentes do sistema operacional (threads de transporte). Eles fornecem uma maneira mais eficiente de lidar com a simultaneidade em comparação com a criação de vários threads de sistema operacional, pois geram menor sobrecarga. A JVM mapeia threads virtuais para threads transportadoras dinamicamente, permitindo uma melhor utilização de recursos.
Gerenciados pela JVM: Ao contrário dos threads do sistema operacional que são gerenciados diretamente pelo sistema operacional, os threads virtuais são criados e agendados pela Java Virtual Machine (JVM). Isso permite controle e otimização mais refinados no ambiente JVM.
Sobrecarga reduzida: criar e gerenciar threads virtuais incorre em sobrecarga significativamente menor em comparação com threads do sistema operacional. Isso ocorre porque a JVM pode gerenciar um conjunto maior de threads virtuais de forma eficiente, utilizando um número menor de threads de sistema operacional subjacentes.
Compatibilidade com código existente: Threads virtuais são projetados para serem perfeitamente integrados ao código Java existente. Eles podem ser usados junto com threads de sistema operacional tradicionais e trabalhar dentro de construções familiares como Executor e ExecutorService para gerenciamento simultâneo.
A figura abaixo mostra a relação entre threads virtuais e threads de plataforma:
A fixação ocorre quando um thread virtual fica vinculado ao seu thread transportador. Basicamente, isso significa que o thread virtual não pode ser preemptado (alternado para outro thread de transporte) enquanto estiver em um estado fixado. Aqui estão cenários comuns que acionam a fixação:
Exemplo de código:
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Main { public static void main(String[] args) throws InterruptedException { final Counter counter = new Counter(); Runnable task = () -> { for (int i = 0; iNeste exemplo, quando um thread virtual entra no bloco sincronizado, ele fica preso ao seu thread transportador, mas isso nem sempre é verdade. A palavra-chave sincronizada do Java por si só não é suficiente para causar fixação de thread em threads virtuais. Para que a fixação de thread ocorra, deve haver um ponto de bloqueio dentro de um bloco sincronizado que faça com que um thread virtual acione o estacionamento e, em última análise, não permita a desmontagem de seu thread transportador. A fixação de threads pode causar uma diminuição no desempenho, pois anularia os benefícios do uso de threads leves/virtuais.
Sempre que um thread virtual encontra um ponto de bloqueio, seu estado é transferido para PARKING. Esta transição de estado é indicada invocando o método VirtualThread.park():
// JDK core code void park() { assert Thread.currentThread() == this; // complete immediately if parking permit available or interrupted if (getAndSetParkPermit(false) || interrupted) return; // park the thread setState(PARKING); try { if (!yieldContinuation()) { // park on the carrier thread when pinned parkOnCarrierThread(false, 0); } } finally { assert (Thread.currentThread() == this) && (state() == RUNNING); } }Vamos dar uma olhada em um exemplo de código para ilustrar esse conceito:
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Main { public static void main(String[] args) { Counter counter = new Counter(); Runnable task = () -> { for (int i = 0; i
O sinalizador -Djdk.tracePinnedThreads=full é um argumento de inicialização da JVM que fornece informações de rastreamento detalhadas sobre a fixação de thread virtual. Quando ativado, ele registra eventos como:
Use esse sinalizador criteriosamente apenas durante sessões de depuração, pois ele introduz sobrecarga de desempenho.
Compile nosso código de demonstração:
javac Main.java
Inicie o código compilado com o sinalizador -Djdk.tracePinnedThreads=full:
java -Djdk.tracePinnedThreads=full Main
Observe a saída no console, que mostra informações detalhadas sobre a fixação de thread virtual:
Thread[#29,ForkJoinPool-1-worker-1,5,CarrierThreads] java.base/java.lang.VirtualThread$VThreadContinuation.onPinned(VirtualThread.java:183) java.base/jdk.internal.vm.Continuation.onPinned0(Continuation.java:393) java.base/java.lang.VirtualThread.parkNanos(VirtualThread.java:621) java.base/java.lang.VirtualThread.sleepNanos(VirtualThread.java:791) java.base/java.lang.Thread.sleep(Thread.java:507) Counter.increment(Main.java:38)
A fixação é um cenário indesejável que impede o desempenho de threads virtuais. As travas reentrantes servem como uma ferramenta eficaz para neutralizar a fixação. Veja como você pode usar bloqueios reentrantes para mitigar situações de fixação:
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.locks.ReentrantLock; public class Main { public static void main(String[] args) { Counter counter = new Counter(); Runnable task = () -> { for (int i = 0; iNo exemplo atualizado, usamos um ReentrantLock em vez de um bloco sincronizado. O thread pode adquirir o bloqueio e liberá-lo imediatamente após concluir sua operação, reduzindo potencialmente a duração da fixação em comparação com um bloco sincronizado que pode reter o bloqueio por um período mais longo.
Para concluir
Os threads virtuais do Java são um testemunho da evolução e das capacidades da linguagem. Eles oferecem uma alternativa nova e leve aos threads de sistema operacional tradicionais, fornecendo uma ponte para o gerenciamento eficiente de simultaneidade. Reservar um tempo para se aprofundar e compreender conceitos-chave, como fixação de threads, pode equipar os desenvolvedores com o conhecimento para aproveitar todo o potencial desses threads leves. Esse conhecimento não apenas prepara os desenvolvedores para aproveitar os recursos futuros, mas também os capacita a resolver problemas complexos de controle de simultaneidade de maneira mais eficaz em seus projetos atuais.
Isenção de responsabilidade: Todos os recursos fornecidos são parcialmente provenientes da Internet. Se houver qualquer violação de seus direitos autorais ou outros direitos e interesses, explique os motivos detalhados e forneça prova de direitos autorais ou direitos e interesses e envie-a para o e-mail: [email protected]. Nós cuidaremos disso para você o mais rápido possível.
Copyright© 2022 湘ICP备2022001581号-3