Java Academy · Lektion

Faldgruber: pinning og ThreadLocals

Undgå at blokere carrier-tråde

Lektion 4 af 413 trin

Faldgruber: pinning og ThreadLocals er en gratis Java Academy-lektion på CoddyKit. Dette er lektion 4 af 4. Du kan læse hele lektionen gratis nedenfor — og derefter øve dig praktisk i browseren med en indbygget kodeeditor og en AI-vejleder, der er tilgængelig døgnet rundt. Den er en del af læringsforløbet i Java Academy, og dine fremskridt synkroniseres på tværs af nettet og CoddyKit-appen. Java Academy-kurset indeholder 4 lektioner i alt.

Det, der kan gå galt

Virtuelle tråde er effektive, men to faldgruber kan stille og roligt undergrave deres skalerbarhed: fastlåsning og overdreven brug af ThreadLocal.

Denne lektion forklarer begge dele og viser, hvordan du undgår dem.

Hvad er fastlåsning

Fastlåsning opstår, når en virtuel tråd ikke kan afmonteres fra sin bærertråd, mens den er blokeret. Bærerens OS-tråd forbliver fastlåst, hvilket fjerner fordelen ved den skalerbarhed, virtuelle tråde giver.

Når for mange bærertråde er fastlåst, kollapser gennemløbet.

Årsag: blokering i synchronized

Den klassiske årsag er blokering inde i en synchronized-blok eller -metode. Når den virtuelle tråd holder monitoren og derefter blokerer, forbliver den fastlåst til sin bærertråd.

Bemærk: I JDK 24+ (JEP 491) blev denne begrænsning i vid udstrækning fjernet, men i Java 21 er den et reelt problem.

Eksempel på fastlåsning, du bør undgå

Dette mønster kan give fastlåsning i Java 21: blokering, mens en monitor holdes. Koden kører stadig korrekt, men den skalerer ikke.

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

Løsningen: ReentrantLock

Erstat synchronized med ReentrantLock, når det kritiske afsnit kan blokere. Låse-API'er er venlige over for virtuelle tråde og tillader afmontering.

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

Diagnosticering af fastlåsning

Du kan bede JVM'en om at udskrive et stack-trace, hver gang der opstår fastlåsning, ved at starte med:

  • -Djdk.tracePinnedThreads=full for komplette stack-traces
  • -Djdk.tracePinnedThreads=short for korte enlinjer

Det hjælper dig med at finde de synchronized-afsnit, der forårsager problemet.

ThreadLocal bliver dyrt

ThreadLocal gemmer tilstand pr. tråd. Med nogle få platformtråde er det fint. Med millioner af virtuelle tråde, der hver har deres egen kopi, vokser hukommelsesforbruget voldsomt.

Gennemgå dine biblioteker: caching, formatering og kontekstobjekter skjuler ofte ThreadLocal.

ThreadLocal fungerer stadig

ThreadLocal er ikke forbudt, men brug det sparsomt. Hver virtuel tråd får som forventet sin egen værdi.

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

Foretræk ScopedValue

Java introducerede ScopedValue som et lettere alternativ til at dele uforanderlige data med en afgrænset levetid. Det undgår omkostningen ved foranderlig lagring pr. tråd i ThreadLocal og passer til modellen for struktureret samtidighed.

Hvis du kun har brug for at føre skrivebeskyttet kontekst ned gennem en kaldkæde, bør du bruge det i stedet.

Opret ikke en pulje

En anden subtil faldgrube er at behandle virtuelle tråde som en knap ressource og oprette en pulje af dem. Det genindfører konkurrence om ressourcer og lækage af ThreadLocal-værdier mellem opgaver.

Opret altid én virtuel tråd pr. opgave, og lad den afslutte.

Tjekliste til skalering

Før du tager virtuelle tråde i brug i produktion:

  • Erstat blokerende synchronized med ReentrantLock
  • Aktivér jdk.tracePinnedThreads under test
  • Minimér ThreadLocal, og foretræk ScopedValue
  • Opret aldrig en pulje af virtuelle tråde

Hurtig kontrol

Find den sikre erstatning for en blokerende synchronized-blok.

Opsummering

Du har lært de vigtigste faldgruber:

  • Fastlåsning: blokering i synchronized optager en bærertråd; løs problemet med ReentrantLock
  • Diagnosticér med -Djdk.tracePinnedThreads
  • Omkostningen ved ThreadLocal vokser med antallet af tråde; foretræk ScopedValue
  • Opret aldrig en pulje af virtuelle tråde

Det afslutter kurset i virtuelle tråde.

Gratis at komme i gang

Lær Java med en AI-underviser — gratis

Skriv og kør rigtig kode i din browser, få øjeblikkelig hjælp fra en AI-underviser døgnet rundt, og fortsæt, hvor du slap, på web eller i appen.

Kurser
104
Lektioner
374

Ofte stillede spørgsmål

Er lektionen “Faldgruber: pinning og ThreadLocals” gratis?

Ja — hele teksten til “Faldgruber: pinning og ThreadLocals” kan læses gratis her på nettet. Hvis du vil øve dig interaktivt med en indbygget kodeeditor og en AI-vejleder døgnet rundt og få adgang til resten af Java Academy-kurset, skal du opgradere til CoddyKit PRO. Java Academy-kurset indeholder 4 lektioner i alt.

Hvad lærer jeg i “Faldgruber: pinning og ThreadLocals”?

Undgå at blokere carrier-tråde Du øver dig i Java Academy med praktisk kode, som du kører direkte i browseren, og en AI-vejleder døgnet rundt besvarer dine spørgsmål, mens du arbejder dig gennem lektionen.

Skal jeg have erfaring for at begynde på Java Academy?

Der kræves ingen tidligere erfaring. Java Academy på CoddyKit er tilrettelagt for både begyndere og øvede, så du kan starte her eller fra begyndelsen og lære i dit eget tempo. Dette er lektion 4 af 4.

Hvor lang tid tager lektionen “Faldgruber: pinning og ThreadLocals”?

De fleste CoddyKit-lektioner tager cirka 5–10 minutter. Hver lektion er kort og interaktiv, så du gør løbende fremskridt og kan fortsætte, hvor du slap – på både web og app.

Kan jeg skrive og køre kode i denne Java Academy-lektion?

Ja. Alle Java Academy-lektioner har en indbygget kodeeditor, så du kan skrive og køre rigtig kode direkte i din browser og få øjeblikkelig feedback fra AI – uden lokal opsætning.

Alle lektioner i dette kursus

  1. Hvad er virtuelle tråde
  2. Oprettelse af virtuelle tråde
  3. Platformtråde kontra virtuelle tråde
  4. Faldgruber: pinning og ThreadLocals
← Tilbage til Java Academy