Careeroza
Parallel Streams
How They Work
parallelStream() or .parallel() splits the data into chunks, processes them on the common ForkJoinPool (one thread per CPU core minus one, plus the caller) and combines the results.
[1 .. 1,000,000]
│ split
┌───┴────┐
[1..500k] [500k..1M] ← processed on different cores
│ split │ split
... ...
└──┬───┘ combine (sum)
resultOrder and Results
Example
Results like sums and toList() keep the correct order. forEach on a parallel stream prints in random order, so use forEachOrdered when order matters.
import java.util.*;
import java.util.stream.*;
public class Main {
public static void main(String[] args) {
long serial = LongStream.rangeClosed(1, 2_000_000).filter(n -> n % 3 == 0).sum();
long parallel = LongStream.rangeClosed(1, 2_000_000).parallel().filter(n -> n % 3 == 0).sum();
System.out.println("Same result: " + (serial == parallel) + " (" + parallel + ")");
List<Integer> doubled = IntStream.rangeClosed(1, 10).parallel().map(n -> n * 2).boxed().toList();
System.out.println("toList keeps encounter order: " + doubled);
System.out.print("forEachOrdered: ");
IntStream.rangeClosed(1, 10).parallel().forEachOrdered(n -> System.out.print(n + " "));
System.out.println();
}
}Output
Same result: true (666666333333) toList keeps encounter order: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20] forEachOrdered: 1 2 3 4 5 6 7 8 9 10
When to Use
| Good fit | Bad fit |
|---|---|
| Large data (100k+ elements), CPU-heavy work per element | Small lists: thread overhead is bigger than the gain |
| ArrayList, arrays, IntStream.range (split easily) | LinkedList, Stream.iterate (split badly) |
| Stateless, independent operations | Shared mutable state, synchronized blocks |
| Associative reductions (sum, max) | Blocking I/O (database, HTTP calls) — use an executor or virtual threads |
Note: Never add to an ArrayList from a parallel forEach: it is not thread-safe and you will lose elements or get exceptions. Use collect / toList() instead. Always measure before and after switching to parallel.