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=fullper le tracce complete-Djdk.tracePinnedThreads=shortper 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
synchronizedbloccanti conReentrantLock - Abilitate
jdk.tracePinnedThreadsdurante i test - Riducete al minimo
ThreadLocal; preferiteScopedValue - 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
synchronizedoccupa un carrier; risolvete il problema conReentrantLock - 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
- Cosa sono i virtual thread
- Creare virtual thread
- Thread di piattaforma e virtual thread
- Problemi: pinning e ThreadLocal