0Pricing
Java Academy · Lektion

Thread-Sicherheit und Seiteneffekte

Gemeinsam genutzten veränderlichen Zustand vermeiden

Thread-Sicherheit und Seiteneffekte ist eine kostenlose Java Academy-Lektion auf CoddyKit. Dies ist Lektion 3 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.

Parallelität bedeutet nebenläufigen Zugriff

In einem parallelen Stream führen mehrere Threads Ihre Lambdas gleichzeitig aus. Jeder von ihnen berührte gemeinsam genutzte veränderliche Zustand führt zu einer Race Condition.

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

Die Gefahr: Eine gemeinsam genutzte Variable verändern

Ein einfacher int-Zähler, auf den mehrere Threads zugreifen, verliert Aktualisierungen. Akkumulieren Sie innerhalb von forEach niemals in gemeinsam genutztem veränderlichem Zustand.

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

Warum einfache Collections scheitern

Das Hinzufügen zu einer nicht threadsicheren ArrayList aus einem parallelen forEach kann die Collection beschädigen oder Elemente verlieren. Die Lösung besteht darin, Daten überhaupt nicht gemeinsam zu verwenden und zu verändern.

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

Das richtige Werkzeug: collect

Verwenden Sie collect statt eines forEach mit Seiteneffekten. Das Collector-Framework sammelt Teilergebnisse sicher threadweise in separaten Containern und führt diese zusammen.

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

Das richtige Werkzeug: reduce

reduce ist von sich aus threadsicher, wenn die Operation assoziativ und zustandslos ist, da Teilergebnisse ohne gemeinsam genutzte Änderungen zusammengeführt werden.

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

Zustandslose Lambdas

Lambdas in Streams sollten zustandslos sein: Ihr Ergebnis hängt nur von der Eingabe ab, nicht von externen veränderlichen Variablen oder vorherigen Elementen.

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

Atomare Typen als Ausweg

Wenn Sie tatsächlich einen Zähler gemeinsam verwenden müssen, nutzen Sie atomare Klassen wie AtomicLong. Sie serialisieren Aktualisierungen korrekt, verursachen allerdings zusätzliche Konkurrenz um die Ressource.

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

Nebenläufige Collections

Wenn Sie eine Map zwischen Threads gemeinsam verwenden müssen, ist eine ConcurrentHashMap sicher. Bevorzugen Sie dennoch nach Möglichkeit Collectors.groupingByConcurrent.

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

Von reihenfolgeabhängiger Logik absehen

Verlassen Sie sich innerhalb paralleler Lambdas nicht auf die Reihenfolge der Elemente. Operationen, die eine sequenzielle Verarbeitung voraussetzen, liefern parallel falsche Ergebnisse.

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

Reine Funktionen lassen sich sicher parallelisieren

Die wichtigste Regel: Schreiben Sie reine, zustandslose und nicht interferierende Operationen. Dann kann ein Stream sequenziell oder parallel ausgeführt werden und liefert identische, korrekte Ergebnisse.

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

Keine Eingriffe

Verändern Sie die Quell-Collection nicht, während ein Stream sie verarbeitet. Dies führt unabhängig von sequenzieller oder paralleler Verarbeitung zu einer ConcurrentModificationException oder undefiniertem Verhalten.

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

Kurze Prüfung

Wie sollten Ergebnisse aus einem parallelen Stream empfohlen gesammelt werden?

Zusammenfassung

Sie haben parallele Streams korrekt gehalten:

  • Gemeinsam genutzter veränderlicher Zustand in parallelen Lambdas verursacht Race Conditions.
  • Bevorzugen Sie collect und reduce gegenüber einem forEach mit Seiteneffekten.
  • Halten Sie Lambdas zustandslos, rein und nicht interferierend.
  • Wenn eine gemeinsame Nutzung unvermeidbar ist, verwenden Sie atomare oder nebenläufige Collections – dies sollte jedoch das letzte Mittel sein.

Häufig gestellte Fragen

Ist die Lektion „Thread-Sicherheit und Seiteneffekte“ kostenlos?

Ja — der vollständige Text von „Thread-Sicherheit und Seiteneffekte“ 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 „Thread-Sicherheit und Seiteneffekte“?

Gemeinsam genutzten veränderlichen Zustand 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 3 von 4.

Wie lange dauert die Lektion „Thread-Sicherheit und Seiteneffekte“?

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. Parallele Streams erstellen
  2. Wann Parallelität hilft
  3. Thread-Sicherheit und Seiteneffekte
  4. Häufige Fallstricke
← Zurück zu Java Academy