Appearance
Parallel Streams β
Zusammenfassung β
Parallel Streams verarbeiten Daten gleichzeitig auf mehreren CPU-Kernen statt sequenziell. Sie nutzen automatisch das Fork/Join-Framework, um groΓe Datenmengen schneller zu verarbeiten.
Kernkonzept β
Parallel Streams teilen die Datenverarbeitung in mehrere Aufgaben auf und verteilen diese auf verfΓΌgbare Prozessorkerne. Das funktioniert transparent β du nutzt die gleiche API wie bei normalen Streams, musst aber .parallel() aufrufen.
Das Fork/Join-Framework kΓΌmmert sich um die Verwaltung von Threads und Task-Verteilung. Es teilt Daten rekursiv auf (Fork), verarbeitet sie parallel und kombiniert die Ergebnisse wieder (Join).
Achtung: Parallel Streams haben Overhead durch Thread-Management. Sie lohnen sich nur bei groΓen Datenmengen (typisch > 10.000 Elemente) oder aufwΓ€ndigen Operationen.
Code-Beispiel β
java
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8);
// Sequenziell (Standard)
int sum = numbers.stream()
.filter(n -> n % 2 == 0)
.map(n -> n * n)
.reduce(0, Integer::sum);
// Parallel
int parallelSum = numbers.parallelStream()
.filter(n -> n % 2 == 0)
.map(n -> n * n)
.reduce(0, Integer::sum);
// Oder mit .parallel()
int sum2 = numbers.stream()
.parallel()
.filter(n -> n % 2 == 0)
.map(n -> n * n)
.reduce(0, Integer::sum);Wichtige Punkte β
- Thread-Pool: Nutzt den gemeinsamen
ForkJoinPool.commonPool()β nicht zu viele parallele Streams gleichzeitig! - Reihenfolge:
.parallel()kann die Verarbeitungsreihenfolge Γ€ndern β nicht geeignet, wenn Order wichtig ist - Stateless Operations: Verwende nur stateless Operationen (keine AbhΓ€ngigkeiten zwischen Elementen)
- Overhead vs. Gewinn: Bei kleinen Listen ist parallel langsamer als sequenziell
- Seiteneffekte vermeiden: Keine
.forEach()mit Shared State β nutze stattdessen.collect()
Klassische Fragen β
Wann sollte ich parallel Streams verwenden? β
Bei groΓen Datenmengen (Richtlinie: > 10.000 Elemente) mit teuren Operationen und einer Verarbeitungslogik, die thread-safe und unabhΓ€ngig fΓΌr jedes Element ist. Bei kleinen Listen oder I/O-Operationen ist der Overhead grΓΆΓer als der Nutzen.
Warum ist my .forEach() mit parallel Streams unsicher? β
.forEach() auf parallelen Streams kann zu Race Conditions fΓΌhren, wenn die Lambda externe State modifiziert. Nutze stattdessen .collect() mit einem thread-sicheren Collector, z.B. toList() oder toMap().
Kann ich die ParallelitΓ€t kontrollieren? β
Nicht direkt ΓΌber Streams. Du kannst aber ein eigenes ForkJoinPool mit spezifischer ParallelitΓ€t erstellen und .execute() nutzen. FΓΌr die meisten FΓ€lle reicht der commonPool().
Wusstest du schon? β
Das Fork/Join-Framework wurde ursprΓΌnglich von Doug Lea entwickelt und ist eine Meisterleistung der Concurrent Programming. Ein Parallel Stream auf einem Single-Core-System kann langsamer sein als der sequenzielle β aber die JVM wird nicht crashen. ParallelitΓ€t ist kein Allheilmittel, sondern erfordert sorgfΓ€ltige Messung mit JMH (Java Microbenchmark Harness)!