Java Academy · leksjon

Trådsikkerhet og bivirkninger

Unngå delt, muterbar tilstand

Leksjon 3 av 413 trinn

Trådsikkerhet og bivirkninger er en gratis leksjon i Java Academy på CoddyKit. Dette er leksjon 3 av 4. Du kan lese hele leksjonen gratis nedenfor – og deretter øve praktisk i nettleseren med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i Java Academy, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i Java Academy inneholder totalt 4 leksjoner.

Parallellitet betyr samtidig tilgang

I en parallell strøm kjører flere tråder lambdaene samtidig. All felles muterbar tilstand som de berører, blir en kappløpstilstand.

import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        int safe = IntStream.rangeClosed(1, 1000).parallel().sum();
        System.out.println(safe);
    }
}

Faren: Endring av en delt variabel

En vanlig int-teller som berøres av mange tråder, mister oppdateringer. Akkumuler aldri i felles muterbar tilstand inne i forEach.

import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        AtomicInteger counter = new AtomicInteger();
        IntStream.rangeClosed(1, 1000).parallel()
            .forEach(n -> counter.incrementAndGet());
        System.out.println(counter.get());
    }
}

Hvorfor vanlige samlinger svikter

Hvis De legger til elementer i en trådsikker ArrayList fra en parallell forEach, kan samlingen bli ødelagt eller miste elementer. Løsningen er å ikke dele og endre tilstanden i det hele tatt.

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        List<Integer> result = IntStream.rangeClosed(1, 10).parallel()
            .boxed()
            .collect(Collectors.toList());
        System.out.println(result.size());
    }
}

Det riktige verktøyet: collect

Bruk collect i stedet for en forEach med bivirkninger. Samlerammeverket akkumulerer delvise beholdere trygt per tråd og slår dem sammen.

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        List<Integer> squares = IntStream.rangeClosed(1, 6).parallel()
            .map(n -> n * n)
            .boxed()
            .collect(Collectors.toList());
        System.out.println(squares);
    }
}

Det riktige verktøyet: reduce

reduce er i seg selv trådsikker når operasjonen er assosiativ og tilstandsløs, fordi den slår sammen delresultater uten felles mutering.

import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        int sum = IntStream.rangeClosed(1, 1000).parallel()
            .reduce(0, Integer::sum);
        System.out.println(sum);
    }
}

Tilstandsløse lambdaer

Lambdaer i strømmer bør være tilstandsløse: Resultatet deres avhenger bare av inndataene, ikke av eksterne muterbare variabler eller tidligere elementer.

import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        long count = IntStream.rangeClosed(1, 1000).parallel()
            .filter(n -> n % 2 == 0)
            .count();
        System.out.println(count);
    }
}

Atomiske typer som nødløsning

Hvis De virkelig må dele en teller, kan De bruke atomiske klasser som AtomicLong. De serialiserer oppdateringer korrekt, men gir økt konkurranse om ressursen.

import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        AtomicLong sum = new AtomicLong();
        IntStream.rangeClosed(1, 1000).parallel()
            .forEach(sum::addAndGet);
        System.out.println(sum.get());
    }
}

Samtidige samlinger

Når De trenger et delt kart på tvers av tråder, er en ConcurrentHashMap trygg. Foretrekk likevel Collectors.groupingByConcurrent når det er mulig.

import java.util.Map;
import java.util.List;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
        List<String> words = List.of("a", "bb", "cc", "ddd");
        Map<Integer, List<String>> byLen = words.parallelStream()
            .collect(Collectors.groupingByConcurrent(String::length));
        System.out.println(byLen);
    }
}

Unngå rekkefølgeavhengig logikk

Ikke stol på rekkefølgen elementene ble møtt i, inne i parallelle lambdaer. Operasjoner som forutsetter sekvensiell behandling, gir feil resultater parallelt.

import java.util.stream.IntStream;

public class Main {
    public static void main(String[] args) {
        int sum = IntStream.rangeClosed(1, 100).parallel()
            .map(n -> n * 2)
            .sum();
        System.out.println(sum);
    }
}

Rene funksjoner kan parallelliseres trygt

Hovedregelen er å skrive rene, tilstandsløse og ikke-interfererende operasjoner. Da kan en strøm være sekvensiell eller parallell og likevel gi identiske, korrekte resultater.

import java.util.List;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
        List<String> in = List.of("red", "green", "blue");
        String out = in.parallelStream()
            .map(String::toUpperCase)
            .sorted()
            .collect(Collectors.joining(","));
        System.out.println(out);
    }
}

Ikke-interferens

Ikke endre kilde­samlingen mens en strøm kjører over den. Dette fører til ConcurrentModificationException eller udefinert oppførsel, både sekvensielt og parallelt.

import java.util.List;
import java.util.stream.Collectors;

public class Main {
    public static void main(String[] args) {
        List<Integer> src = List.of(1, 2, 3, 4);
        List<Integer> doubled = src.parallelStream()
            .map(n -> n * 2)
            .collect(Collectors.toList());
        System.out.println(doubled);
    }
}

Rask kontroll

Hva er den anbefalte måten å akkumulere resultater fra en parallell strøm på?

Oppsummering

De har beholdt parallelle strømmer korrekte:

  • Felles muterbar tilstand i parallelle lambdaer fører til kappløp.
  • Foretrekk collect og reduce fremfor forEach med bivirkninger.
  • Hold lambdaer tilstandsløse, rene og ikke-interfererende.
  • Hvis De må dele tilstand, kan De bruke atomiske eller samtidige samlinger, men dette bør være siste utvei.
Gratis å komme i gang

Lær deg Java med en AI-veileder – gratis

Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.

Kurs
104
Leksjoner
374

Ofte stilte spørsmål

Er leksjonen «Trådsikkerhet og bivirkninger» gratis?

Ja – hele teksten i «Trådsikkerhet og bivirkninger» er gratis å lese her på nettet. For å øve interaktivt med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt, og for å låse opp resten av Java Academy-kurset, kan du oppgradere til CoddyKit PRO. Kurset i Java Academy inneholder totalt 4 leksjoner.

Hva lærer jeg i «Trådsikkerhet og bivirkninger»?

Unngå delt, muterbar tilstand Du øver på Java Academy med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.

Trenger jeg erfaring for å begynne med Java Academy?

Ingen tidligere erfaring er nødvendig. Java Academy på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 3 av 4.

Hvor lang tid tar leksjonen «Trådsikkerhet og bivirkninger»?

De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.

Kan jeg skrive og kjøre kode i denne Java Academy-leksjonen?

Ja. Alle Java Academy-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.

Alle leksjonene i dette kurset

  1. Opprette parallelle strømmer
  2. Når parallellitet hjelper
  3. Trådsikkerhet og bivirkninger
  4. Vanlige fallgruver
← Tilbake til Java Academy