Java Academy · Lektion

Fallstricke: Pinning und ThreadLocals

Blockierende Carrier-Threads vermeiden

Lektion 4 von 413 Schritte

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=full für vollständige Traces
  • -Djdk.tracePinnedThreads=short fü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 synchronized durch ReentrantLock
  • Aktivieren Sie jdk.tracePinnedThreads in Tests
  • Minimieren Sie ThreadLocal; bevorzugen Sie ScopedValue
  • 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 synchronized bindet einen Träger; beheben Sie das mit ReentrantLock
  • 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.

Kostenlos starten

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

  1. Was sind virtuelle Threads
  2. Virtuelle Threads erstellen
  3. Plattform- vs. virtuelle Threads
  4. Fallstricke: Pinning und ThreadLocals
← Zurück zu Java Academy