Java Academy · Oppitunti

Sudenkuopat: pinning ja ThreadLocalit

Vältä kuljettajasäikeiden estämistä.

Oppitunti 4/413 vaihetta

Sudenkuopat: pinning ja ThreadLocalit on ilmainen Java Academy-oppitunti CoddyKitissä. Tämä on oppitunti 4/4. Voit lukea koko oppitunnin alta ilmaiseksi ja harjoitella sen jälkeen käytännössä selaimessa sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla. Oppitunti kuuluu Java Academy-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. Java Academy-kurssilla on yhteensä 4 oppituntia.

Mikä voi mennä vikaan

Virtuaalisäikeet ovat tehokkaita, mutta kaksi ongelmakohtaa voi huomaamatta heikentää niiden skaalautuvuutta: pinning ja ThreadLocal-muuttujien liiallinen käyttö.

Tässä oppitunnissa selitetään molemmat ja kerrotaan, miten niitä voi välttää.

Mitä pinning tarkoittaa

Pinning tapahtuu, kun virtuaalisäie ei voi irrottautua kantajasäikeestään estyessään. Kantajana toimiva käyttöjärjestelmäsäie jää jumiin, mikä kumoaa skaalautuvuuden tuoman hyödyn.

Kun liian monet kantajasäikeet ovat kiinnittyneinä, suorituskyky romahtaa.

Syy: estyminen synchronized-rakenteessa

Klassinen syy on estyminen synchronized-lohkon tai -metodin sisällä. Kun virtuaalisäie pitää monitoria hallussaan ja estyy sen jälkeen, se pysyy kiinnittyneenä kantajasäikeeseensä.

Huomautus: JDK 24:ssä ja sitä uudemmissa versioissa (JEP 491) tämä rajoitus poistettiin suurelta osin, mutta Java 21:ssä se on todellinen huolenaihe.

Vältettävä pinning-esimerkki

Tämä rakenne voi aiheuttaa pinningiä Java 21:ssä: estyminen monitorin ollessa hallussa. Koodi toimii edelleen oikein, mutta se ei skaalaudu.

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

Korjaus: ReentrantLock

Korvatkaa synchronized ReentrantLock-lukolla, kun kriittinen osio voi estyä. Lukkojen ohjelmointirajapinnat sopivat virtuaalisäikeille ja sallivat niiden irrottautumisen kantajasäikeestä.

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

Pinningin diagnosointi

Voitte pyytää JVM:ää tulostamaan pinningiä esiintyessä pinon jäljityksen käynnistämällä ohjelman seuraavasti:

  • -Djdk.tracePinnedThreads=full täydellisiä jäljityksiä varten
  • -Djdk.tracePinnedThreads=short yksirivisiä tietoja varten

Näin löydätte ongelmalliset synchronized-osiot.

ThreadLocalit käyvät kalliiksi

ThreadLocal tallentaa säiekohtaista tilaa. Muutaman alustasäikeen tapauksessa se ei ole ongelma. Kun virtuaalisäikeitä on miljoonia ja jokainen sisältää oman kopionsa, muistin käyttö kasvaa valtavasti.

Tarkistakaa kirjastonne: välimuistit, muotoilu ja kontekstioliot piilottavat usein ThreadLocal-muuttujia.

ThreadLocal toimii edelleen

ThreadLocal ei ole kielletty, mutta käyttäkää sitä säästeliäästi. Jokainen virtuaalisäie saa odotetusti oman arvonsa.

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

Suosikaa ScopedValuea

Java esitteli ScopedValue-arvon kevyempänä vaihtoehtona muuttumattoman tiedon jakamiseen rajatun elinkaaren ajan. Se välttää ThreadLocalin säiekohtaisen muuttuvan tallennustilan kustannukset ja sopii rakenteisen samanaikaisuuden malliin.

Kun tarvitsette vain välittää vain luku -muotoista kontekstia kutsuketjua pitkin, käyttäkää mieluummin sitä.

Älkää poolittako

Toinen hienovarainen ongelma on kohdella virtuaalisäikeitä niukkana resurssina ja muodostaa niistä pooli. Se palauttaa kilpailutilanteet ja tehtävien välisen ThreadLocal-tietojen vuotamisen.

Luokaa aina yksi virtuaalisäie kutakin tehtävää varten ja antakaa sen päättyä.

Skaalautuvuuden tarkistuslista

Ennen kuin viette virtuaalisäikeet tuotantoon:

  • Korvatkaa estävä synchronized ReentrantLock-lukolla
  • Ottakaa jdk.tracePinnedThreads käyttöön testauksessa
  • Minimoikaa ThreadLocal-muuttujien käyttö ja suosikaa ScopedValue-arvoa
  • Älkää koskaan poolittako virtuaalisäikeitä

Pikatarkistus

Tunnistakaa turvallinen korvaaja estävälle synchronized-lohkolle.

Kertaus

Opitte tärkeimmät ongelmakohdat:

  • Pinning: synchronized-rakenteen sisällä estyminen sitoo kantajasäikeen; korjatkaa tilanne käyttämällä ReentrantLock-lukkoa
  • Diagnosoikaa ongelma komennolla -Djdk.tracePinnedThreads
  • ThreadLocal-muuttujien aiheuttama kuormitus kasvaa säiemäärän mukana; suosikaa ScopedValue-arvoa
  • Älkää koskaan poolittako virtuaalisäikeitä

Tähän päättyy Virtual Threads -kurssi.

Aloita maksutta

Opi Java tekoälytuutorin avulla — ilmaiseksi

Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.

Kurssit
104
Oppitunnit
374

Usein kysytyt kysymykset

Onko oppitunti ”Sudenkuopat: pinning ja ThreadLocalit” ilmainen?

Kyllä – oppitunnin ”Sudenkuopat: pinning ja ThreadLocalit” koko tekstin voi lukea täällä verkossa ilmaiseksi. Jos haluat harjoitella interaktiivisesti sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla sekä avata koko Java Academy-kurssin, päivitä CoddyKit PROhon. Java Academy-kurssilla on yhteensä 4 oppituntia.

Mitä opin oppitunnilla ”Sudenkuopat: pinning ja ThreadLocalit”?

Vältä kuljettajasäikeiden estämistä. Harjoittelet Java Academy-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.

Tarvitsenko kokemusta aloittaakseni Java Academy-opiskelun?

Aiempi kokemus ei ole tarpeen. CoddyKitin Java Academy-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 4/4.

Kuinka kauan ”Sudenkuopat: pinning ja ThreadLocalit”-oppitunnin suorittaminen kestää?

Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.

Voinko kirjoittaa ja suorittaa koodia tällä Java Academy-oppitunnilla?

Kyllä. Jokainen Java Academy-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.

Kaikki tämän kurssin oppitunnit

  1. Mitä virtuaalisäikeet ovat
  2. Virtuaalisäikeiden luonti
  3. Alusta- ja virtuaalisäikeet
  4. Sudenkuopat: pinning ja ThreadLocalit
← Takaisin: Java Academy