Fallstricke: Pinning und ThreadLocals
Blockierende Carrier-Threads vermeiden
Fallstricke: Pinning und ThreadLocals ist eine kostenlose Java Academy-Lektion auf CoddyKit. Dies ist Lektion 4 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des Java Academy-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der Java Academy-Kurs umfasst insgesamt 4 Lektionen.
Was schiefgehen kann
Virtuelle Threads sind leistungsstark, aber zwei Tücken können ihre Skalierbarkeit unbemerkt beeinträchtigen: Pinning und der übermäßige Einsatz von ThreadLocals.
In dieser Lektion erfahren Sie mehr über beide und darüber, wie Sie sie vermeiden.
Was ist Pinning
Pinning tritt auf, wenn ein virtueller Thread während einer Blockierung nicht von seinem Träger entkoppelt werden kann. Der Betriebssystem-Thread des Trägers bleibt blockiert, wodurch der Skalierbarkeitsvorteil verloren geht.
Wenn zu viele Träger blockiert sind, bricht der Durchsatz ein.
Ursache: Blockieren in synchronized
Die klassische Ursache ist eine Blockierung innerhalb eines synchronized-Blocks oder einer synchronized-Methode. Wenn der virtuelle Thread den Monitor hält und anschließend blockiert, bleibt er an seinen Träger gebunden.
Hinweis: Ab JDK 24 (JEP 491) wurde diese Einschränkung weitgehend beseitigt, aber unter Java 21 ist sie ein echtes Problem.
Zu vermeidendes Pinning-Beispiel
Dieses Muster kann unter Java 21 zu Pinning führen: Blockieren, während ein Monitor gehalten wird. Der Code läuft weiterhin korrekt, skaliert aber nicht.
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)");
}
}Die Lösung: ReentrantLock
Ersetzen Sie synchronized durch einen ReentrantLock, wenn der kritische Abschnitt blockieren kann. Lock-APIs sind für virtuelle Threads geeignet und ermöglichen das Entkoppeln vom Träger.
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");
}
}Pinning diagnostizieren
Sie können die JVM anweisen, bei jedem Auftreten von Pinning einen Stacktrace auszugeben, indem Sie sie mit folgenden Optionen starten:
-Djdk.tracePinnedThreads=fullfür vollständige Traces-Djdk.tracePinnedThreads=shortfür einzeilige Meldungen
So finden Sie die problematischen synchronized-Abschnitte.
ThreadLocals werden teuer
ThreadLocal speichert einen Zustand pro Thread. Bei einigen wenigen Plattform-Threads ist das unproblematisch. Bei Millionen virtueller Threads, von denen jeder seine eigene Kopie enthält, steigt der Speicherbedarf jedoch stark an.
Überprüfen Sie Ihre Bibliotheken: Caching, Formatierung und Kontextobjekte verbergen häufig ThreadLocals.
ThreadLocal funktioniert weiterhin
ThreadLocal ist nicht verboten, Sie sollten es nur sparsam einsetzen. Jeder virtuelle Thread erhält wie erwartet seinen eigenen Wert.
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();
}
}ScopedValue bevorzugen
Java hat ScopedValue als leichtere Alternative eingeführt, um unveränderliche Daten mit begrenzter Lebensdauer gemeinsam zu nutzen. Dadurch entfallen die Kosten des veränderlichen Speichers pro Thread von ThreadLocal, und das Konzept passt zum Modell der strukturierten Nebenläufigkeit.
Wenn Sie lediglich schreibgeschützten Kontext entlang einer Aufrufkette weitergeben müssen, sollten Sie stattdessen darauf zurückgreifen.
Nicht poolen
Eine weitere subtile Tücke besteht darin, virtuelle Threads wie eine knappe Ressource zu behandeln und sie zu poolen. Dadurch führen Sie sowohl Konkurrenz als auch das Austreten von ThreadLocal-Werten zwischen Aufgaben wieder ein.
Erstellen Sie immer einen virtuellen Thread pro Aufgabe und lassen Sie ihn anschließend enden.
Checkliste für Skalierbarkeit
Bevor Sie virtuelle Threads in Produktion einsetzen:
- Ersetzen Sie blockierendes
synchronizeddurchReentrantLock - Aktivieren Sie
jdk.tracePinnedThreadsin Tests - Minimieren Sie
ThreadLocal; bevorzugen SieScopedValue - Poolen Sie virtuelle Threads niemals
Kurze Überprüfung
Ermitteln Sie den sicheren Ersatz für einen blockierenden synchronized-Block.
Zusammenfassung
Sie haben die wichtigsten Tücken kennengelernt:
- Pinning: Eine Blockierung innerhalb von
synchronizedbindet einen Träger; beheben Sie das mitReentrantLock - Diagnostizieren Sie Pinning mit
-Djdk.tracePinnedThreads - Der Aufwand von ThreadLocal steigt mit der Thread-Anzahl; bevorzugen Sie
ScopedValue - Poolen Sie virtuelle Threads niemals
Damit ist der Kurs zu virtuellen Threads abgeschlossen.
Lerne Java mit einem KI-Tutor — kostenlos
Schreibe und führe echten Code in deinem Browser aus, bekomme sofortige Hilfe von einem 24/7 KI-Tutor und setze dein Lernen im Web oder in der App fort.
- Kurse
- 104
- Lektionen
- 374
Häufig gestellte Fragen
Ist die Lektion „Fallstricke: Pinning und ThreadLocals“ kostenlos?
Ja — der vollständige Text von „Fallstricke: Pinning und ThreadLocals“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des Java Academy-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der Java Academy-Kurs umfasst insgesamt 4 Lektionen.
Was lerne ich in „Fallstricke: Pinning und ThreadLocals“?
Blockierende Carrier-Threads vermeiden Du übst Java Academy mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.
Brauche ich Erfahrung, um Java Academy zu starten?
Keine Vorkenntnisse erforderlich. Java Academy auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 4 von 4.
Wie lange dauert die Lektion „Fallstricke: Pinning und ThreadLocals“?
Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.
Kann ich in dieser Java Academy-Lektion Code schreiben und ausführen?
Ja. Jede Java Academy-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.
Alle Lektionen in diesem Kurs
- Was sind virtuelle Threads
- Virtuelle Threads erstellen
- Plattform- vs. virtuelle Threads
- Fallstricke: Pinning und ThreadLocals