Java Academy · Leçon

Pièges : épinglage et ThreadLocals

Évitez de bloquer les threads porteurs

Leçon 4 sur 413 étapes

Pièges : épinglage et ThreadLocals est une leçon Java Academy gratuite sur CoddyKit. Ceci est la leçon 4 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Java Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Java Academy comprend 4 leçons au total.

Ce qui peut mal tourner

Les threads virtuels sont puissants, mais deux pièges peuvent discrètement réduire leur évolutivité : l'épinglage et l'utilisation excessive des ThreadLocals.

Cette leçon présente ces deux problèmes et explique comment les éviter.

Qu'est-ce que l'épinglage

L'épinglage se produit lorsqu'un thread virtuel ne peut pas se détacher de son thread porteur pendant qu'il est bloqué. Le thread de l'OS porteur reste bloqué, ce qui annule le gain d'évolutivité.

Lorsque trop de threads porteurs sont épinglés, le débit s'effondre.

Cause : blocage synchronisé

La cause classique est un blocage à l'intérieur d'un bloc ou d'une méthode synchronized. Lorsqu'il détient le moniteur puis se bloque, le thread virtuel reste épinglé à son thread porteur.

Remarque : à partir de JDK 24 (JEP 491), cette limitation a été en grande partie supprimée, mais elle constitue un réel problème dans Java 21.

Exemple d'épinglage à éviter

Ce modèle peut provoquer un épinglage dans Java 21 : il bloque tout en détenant un moniteur. Il s'exécute toujours correctement, mais ne passe pas à l'échelle.

public class Main {
    static final Object lock = new Object();

    static void risky() {
        synchronized (lock) {
            try { Thread.sleep(10); } catch (InterruptedException e) {}
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread t = Thread.ofVirtual().start(Main::risky);
        t.join();
        System.out.println("Done (but this blocked inside synchronized)");
    }
}

La correction : ReentrantLock

Remplacez synchronized par un ReentrantLock lorsque la section critique peut se bloquer. Les interfaces de verrouillage sont adaptées aux threads virtuels et permettent le détachement.

import java.util.concurrent.locks.ReentrantLock;

public class Main {
    static final ReentrantLock lock = new ReentrantLock();

    static void safe() {
        lock.lock();
        try {
            try { Thread.sleep(10); } catch (InterruptedException e) {}
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread t = Thread.ofVirtual().start(Main::safe);
        t.join();
        System.out.println("Done without pinning");
    }
}

Diagnostiquer l'épinglage

Vous pouvez demander à la JVM d'afficher une trace de pile chaque fois qu'un épinglage se produit en la lançant avec :

  • -Djdk.tracePinnedThreads=full pour obtenir les traces complètes
  • -Djdk.tracePinnedThreads=short pour obtenir des traces d'une ligne

Cela vous aide à localiser les sections synchronized responsables.

Les ThreadLocals deviennent coûteux

ThreadLocal stocke un état propre à chaque thread. Avec quelques threads de plateforme, cela ne pose aucun problème. Avec des millions de threads virtuels, chacun contenant sa propre copie, la consommation mémoire augmente considérablement.

Examinez vos bibliothèques : la mise en cache, le formatage et les objets de contexte dissimulent souvent des ThreadLocals.

ThreadLocal fonctionne toujours

ThreadLocal n'est pas interdit, utilisez-le simplement avec modération. Chaque thread virtuel reçoit bien sa propre valeur, comme prévu.

public class Main {
    static final ThreadLocal<String> CONTEXT = new ThreadLocal<>();

    public static void main(String[] args) throws InterruptedException {
        Thread t = Thread.ofVirtual().start(() -> {
            CONTEXT.set("request-42");
            System.out.println("Context: " + CONTEXT.get());
            CONTEXT.remove();
        });
        t.join();
    }
}

Préférer ScopedValue

Java a introduit ScopedValue comme solution plus légère pour partager des données immuables pendant une durée de vie limitée. Cela évite le coût du stockage mutable propre à chaque thread de ThreadLocal et s'intègre au modèle de concurrence structurée.

Lorsque vous devez seulement transmettre un contexte en lecture seule le long d'une chaîne d'appels, choisissez plutôt cette solution.

Ne regroupez pas les threads

Un autre piège subtil consiste à traiter les threads virtuels comme une ressource rare et à les regrouper. Cela réintroduit de la contention et des fuites de ThreadLocal entre les tâches.

Créez toujours un thread virtuel par tâche et laissez-le se terminer.

Liste de contrôle pour l'évolutivité

Avant de mettre des threads virtuels en production :

  • Remplacez synchronized bloquant par ReentrantLock
  • Activez jdk.tracePinnedThreads lors des vérifications
  • Réduisez l'utilisation de ThreadLocal ; préférez ScopedValue
  • Ne regroupez jamais les threads virtuels

Vérification rapide

Identifiez le remplacement sûr d'un bloc synchronized bloquant.

Récapitulatif

Vous avez appris les principaux pièges :

  • Épinglage : un blocage dans synchronized monopolise un thread porteur ; corrigez-le avec ReentrantLock
  • Diagnostiquez-le avec -Djdk.tracePinnedThreads
  • Le coût de ThreadLocal augmente avec le nombre de threads ; préférez ScopedValue
  • Ne regroupez jamais les threads virtuels

Le cours sur les threads virtuels est maintenant terminé.

Gratuit pour commencer

Apprends Java avec un tuteur IA — gratuit

Écris et exécute du vrai code dans ton navigateur, obtiens de l'aide instantanée d'un tuteur IA disponible 24h/24, et reprends là où tu t'es arrêté sur le web ou dans l'app.

Cours
104
Leçons
374

Questions Fréquemment Posées

La leçon « Pièges : épinglage et ThreadLocals » est-elle gratuite ?

Oui — le texte complet de « Pièges : épinglage et ThreadLocals » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Java Academy, passe à CoddyKit PRO. Le cours Java Academy comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Pièges : épinglage et ThreadLocals » ?

Évitez de bloquer les threads porteurs Tu pratiques Java Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer Java Academy ?

Aucune expérience préalable n'est requise. Java Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 4 sur 4.

Combien de temps prend la leçon « Pièges : épinglage et ThreadLocals » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon Java Academy ?

Oui. Chaque leçon Java Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. Que sont les threads virtuels
  2. Créer des threads virtuels
  3. Threads de plateforme ou virtuels
  4. Pièges : épinglage et ThreadLocals
← Retour à Java Academy