Häufige Fallstricke
Reihenfolge, Boxing und der Common Pool
Häufige Fallstricke ist eine kostenlose Java Academy-Lektion auf CoddyKit. Dies ist Lektion 4 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 ist kein kostenloser Geschwindigkeitsgewinn
Das Hinzufügen von parallel() kann den Code langsamer statt schneller machen. Diese Lektion behandelt die klassischen Fallstricke, damit Sie sie vermeiden können.
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
long sum = IntStream.rangeClosed(1, 1_000_000).parallel()
.asLongStream().sum();
System.out.println(sum);
}
}Fallstrick: Aufwand durch Reihenfolge
Geordnete Operationen wie forEachOrdered oder limit erfordern eine Abstimmung zwischen Threads und schmälern dadurch den Gewinn der Parallelisierung.
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
IntStream.rangeClosed(1, 5).parallel()
.forEachOrdered(System.out::println);
}
}unordered kann helfen
Wenn die Reihenfolge keine Rolle spielt, kann der Aufruf von unordered() der Laufzeit ermöglichen, die Verarbeitung der Reihenfolge zu überspringen und freier zu parallelisieren.
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
long count = IntStream.rangeClosed(1, 1_000_000).parallel()
.unordered()
.filter(n -> n % 5 == 0)
.count();
System.out.println(count);
}
}Fallstrick: Verstecktes Boxing
Die Verwendung von Stream<Integer> in parallelen Streams vervielfacht den Boxing-Aufwand über mehrere Threads. Bevorzugen Sie für numerische parallele Verarbeitung primitive Streams.
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
long sum = IntStream.rangeClosed(1, 1_000_000).parallel().asLongStream().sum();
System.out.println(sum);
}
}Fallstrick: Der gemeinsam genutzte Common Pool
Alle parallelen Streams verwenden denselben ForkJoinPool.commonPool(). Eine lang laufende parallele Aufgabe kann andere Aufgaben in derselben JVM aushungern, einschließlich anderer Teile Ihrer Anwendung.
import java.util.concurrent.ForkJoinPool;
public class Main {
public static void main(String[] args) {
System.out.println(ForkJoinPool.commonPool().getParallelism());
}
}Arbeit in einem eigenen Pool isolieren
Um umfangreiche Aufgaben aus dem Common Pool herauszuhalten, übergeben Sie den parallelen Stream innerhalb Ihres eigenen ForkJoinPool.
import java.util.concurrent.ForkJoinPool;
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) throws Exception {
ForkJoinPool pool = new ForkJoinPool(2);
long sum = pool.submit(() ->
IntStream.rangeClosed(1, 1_000_000).parallel().asLongStream().sum()
).get();
pool.shutdown();
System.out.println(sum);
}
}Fallstrick: Schlecht aufteilbare Quellen
LinkedList, Stream.iterate und BufferedReader.lines lassen sich schlecht aufteilen. Ihre Parallelisierung bringt häufig nur einen geringen oder sogar einen negativen Nutzen.
import java.util.stream.Stream;
public class Main {
public static void main(String[] args) {
long n = Stream.iterate(1, x -> x + 1)
.limit(100_000)
.parallel()
.filter(x -> x % 2 == 0)
.count();
System.out.println(n);
}
}Fallstrick: Zustandsbehaftete Lambdas
Lambdas, die externen Zustand lesen oder schreiben, verhalten sich parallel nichtdeterministisch. Halten Sie sie zustandslos, um konsistente Ergebnisse zu erhalten.
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
int sum = IntStream.rangeClosed(1, 100).parallel()
.map(n -> n + 1)
.sum();
System.out.println(sum);
}
}Fallstrick: findFirst und findAny
findFirst muss die Reihenfolge einhalten, was bei paralleler Verarbeitung zusätzlichen Aufwand verursacht. Wenn ein beliebiges passendes Element genügt, verwenden Sie findAny.
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
int found = IntStream.rangeClosed(1, 1_000_000).parallel()
.filter(n -> n % 99991 == 0)
.findAny()
.getAsInt();
System.out.println(found);
}
}Fallstrick: Günstige Operationen bei kleinen Datenmengen
Bei sehr kleinen Collections oder trivialen Aufgaben überwiegt der Aufwand für Aufteilung und Zusammenführung jeden möglichen Gewinn. Bleiben Sie bei der sequenziellen Verarbeitung.
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> small = List.of(1, 2, 3, 4);
int sum = small.stream().mapToInt(Integer::intValue).sum();
System.out.println(sum);
}
}Fallstrick: Aufwendige Zusammenführung in Collectors
Collectors mit aufwendigen Zusammenführungsschritten, etwa beim Erstellen großer sortierter Strukturen, können den Gewinn der Parallelisierung zunichtemachen. Einfache Akkumulationen lassen sich kostengünstig zusammenführen.
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
long total = IntStream.rangeClosed(1, 1_000_000).parallel()
.mapToLong(n -> n)
.sum();
System.out.println(total);
}
}Kurze Prüfung
Warum kann findFirst in einem parallelen Stream langsamer sein als findAny?
Zusammenfassung
Sie haben die häufigsten Fallstricke paralleler Verarbeitung kennengelernt:
- Reihenfolge (
forEachOrdered,limit,findFirst) verursacht zusätzlichen Abstimmungsaufwand; verwenden Sie nach MöglichkeitunorderedoderfindAny. - Boxing vervielfacht den Aufwand; bevorzugen Sie primitive Streams.
- Der gemeinsam genutzte Common Pool kann andere Aufgaben aushungern; isolieren Sie umfangreiche Aufgaben in einem eigenen
ForkJoinPool. - Schlecht aufteilbare Quellen, kleine Datenmengen und aufwendige Zusammenführungen beeinträchtigen allesamt die Parallelisierung.
Häufig gestellte Fragen
Ist die Lektion „Häufige Fallstricke“ kostenlos?
Ja — der vollständige Text von „Häufige Fallstricke“ 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 „Häufige Fallstricke“?
Reihenfolge, Boxing und der Common Pool 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 4 von 4.
Wie lange dauert die Lektion „Häufige Fallstricke“?
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
- Parallele Streams erstellen
- Wann Parallelität hilft
- Thread-Sicherheit und Seiteneffekte
- Häufige Fallstricke