0Pricing
Java Academy · Lezione

Problemi: pinning e ThreadLocal

Evitare di bloccare i thread carrier

Problemi: pinning e ThreadLocal è una lezione Java Academy gratuita su CoddyKit. Questa è la lezione 4 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento Java Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso Java Academy include 4 lezioni in totale.

Cosa può andare storto

I thread virtuali sono potenti, ma due problemi possono compromettere silenziosamente la loro scalabilità: il pinning e l'uso eccessivo dei ThreadLocal.

Questa lezione spiega entrambi e mostra come evitarli.

Che cos'è il pinning

Il pinning si verifica quando un thread virtuale bloccato non può eseguire l'unmount dal proprio carrier. Il thread del sistema operativo che funge da carrier rimane bloccato, annullando il vantaggio in termini di scalabilità.

Quando troppi carrier sono sottoposti a pinning, la velocità di elaborazione crolla.

Causa: blocco in synchronized

La causa classica è un blocco all'interno di un blocco o metodo synchronized. Se il thread virtuale detiene il monitor e poi si blocca, rimane sottoposto a pinning sul proprio carrier.

Nota: in JDK 24+ (JEP 491) questa limitazione è stata in gran parte rimossa, ma in Java 21 rappresenta un problema concreto.

Esempio di pinning da evitare

Questo schema può causare pinning in Java 21: il blocco mentre si detiene un monitor. Il codice continua a funzionare correttamente, ma non è scalabile.

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 soluzione: ReentrantLock

Sostituite synchronized con ReentrantLock quando la sezione critica può bloccarsi. Le API dei lock sono compatibili con i thread virtuali e consentono l'unmount.

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");
    }
}

Diagnosi del pinning

Potete chiedere alla JVM di stampare uno stack trace ogni volta che si verifica un pinning, avviando il programma con:

  • -Djdk.tracePinnedThreads=full per le tracce complete
  • -Djdk.tracePinnedThreads=short per una riga

Questo vi aiuta a individuare le sezioni synchronized responsabili.

I ThreadLocal diventano costosi

ThreadLocal memorizza uno stato specifico per ogni thread. Con pochi thread di piattaforma non è un problema. Con milioni di thread virtuali, ciascuno con la propria copia, l'uso di memoria aumenta enormemente.

Esaminate le vostre librerie: cache, formattazione e oggetti di contesto spesso nascondono dei ThreadLocal.

ThreadLocal funziona ancora

ThreadLocal non è vietato: utilizzatelo semplicemente con moderazione. Ogni thread virtuale riceve il proprio valore, come previsto.

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();
    }
}

Preferite ScopedValue

Java ha introdotto ScopedValue come alternativa più leggera per condividere dati immutabili con una durata limitata. Evita il costo dello spazio di memoria modificabile per thread di ThreadLocal e si adatta al modello di concorrenza strutturata.

Quando dovete solo trasferire un contesto in sola lettura lungo una catena di chiamate, preferitelo.

Non effettuate il pooling

Un altro problema sottile consiste nel trattare i thread virtuali come una risorsa scarsa e inserirli in un pool. In questo modo reintroducete la contesa e la perdita di dati ThreadLocal tra un task e l'altro.

Create sempre un thread virtuale per ogni task e lasciatelo terminare.

Checklist per la scalabilità

Prima di portare in produzione un'applicazione con thread virtuali:

  • Sostituite synchronized bloccanti con ReentrantLock
  • Abilitate jdk.tracePinnedThreads durante i test
  • Riducete al minimo ThreadLocal; preferite ScopedValue
  • Non effettuate mai il pooling dei thread virtuali

Verifica rapida

Individuate il sostituto sicuro di un blocco synchronized bloccante.

Riepilogo

Avete imparato a conoscere i problemi principali:

  • Pinning: il blocco all'interno di synchronized occupa un carrier; risolvete il problema con ReentrantLock
  • Diagnosticate il problema con -Djdk.tracePinnedThreads
  • Il costo di ThreadLocal cresce con il numero di thread; preferite ScopedValue
  • Non effettuate mai il pooling dei thread virtuali

Con questo si conclude il corso sui thread virtuali.

Domande Frequenti

La lezione «Problemi: pinning e ThreadLocal» è gratuita?

Sì — il testo completo di «Problemi: pinning e ThreadLocal» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso Java Academy, passa a CoddyKit PRO. Il corso Java Academy include 4 lezioni in totale.

Cosa imparerò in «Problemi: pinning e ThreadLocal»?

Evitare di bloccare i thread carrier Eserciti Java Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare Java Academy?

Non è richiesta alcuna esperienza precedente. Java Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 4 di 4.

Quanto tempo richiede la lezione «Problemi: pinning e ThreadLocal»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione Java Academy?

Sì. Ogni lezione Java Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Cosa sono i virtual thread
  2. Creare virtual thread
  3. Thread di piattaforma e virtual thread
  4. Problemi: pinning e ThreadLocal
← Torna a Java Academy