Parallel Streams allow stream operations to run concurrently using multiple CPU cores. Instead of processing elements sequentially, the work is divided into tasks and executed in parallel using the Fork–Join Framework.
Parallel streams can significantly improve performance for large, CPU-intensive operations — but must be used carefully.
list.parallelStream()stream.parallel()Stream.of(1, 2, 3).parallel();Parallel streams use the ForkJoinPool.commonPool (a shared pool of worker threads).
- The stream data is split into chunks.
- Each chunk is processed by different threads.
- Results are combined (reduced) into a final result.
Parallel streams are effective when:
- Dataset is large
- Computation is CPU-bound (not I/O)
- Order is not important
- The work per element is non-trivial
int result = list.parallelStream()
.mapToInt(n -> heavyOperation(n))
.sum();List<Integer> evens =
list.parallelStream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());List<Integer> sorted =
list.parallelStream()
.sorted()
.collect(Collectors.toList());int sum =
list.parallelStream()
.reduce(0, Integer::sum);long start = System.currentTimeMillis();
list.stream().map(n -> n * n).count();
long end = System.currentTimeMillis();long start = System.currentTimeMillis();
list.parallelStream().map(n -> n * n).count();
long end = System.currentTimeMillis();Parallel streams often outperform sequential ones only when:
-
Work per element is expensive
-
Dataset is large (thousands/millions of elements)
Parallel streams should NOT be used in:
Parallel overhead > benefit for small lists.
Operations like forEachOrdered() kill parallelism.
Parallelism does not help for:
- Reading files
- Database queries
- Network calls
Threads become blocked.
This is unsafe:
int[] sum = {0}; // shared mutable state
list.parallelStream().forEach(n -> sum[0] += n); // ❌ race conditionSynchronization kills parallelism.
Parallel streams use ForkJoinPool.commonPool, which may interfere with server threads.
Use a custom pool instead (advanced):
ForkJoinPool pool = new ForkJoinPool(10);
pool.submit(() -> list.parallelStream().forEach(...));| Expression | Meaning |
|---|---|
list.parallelStream() |
Creates a parallel stream directly |
list.stream().parallel() |
Converts sequential → parallel |
Both produce the same type of stream.
Stream pipelines cannot switch back and forth improperly.
This is valid:
stream.parallel().filter(...).map(...).sequential().forEach(...);The last call (parallel() or sequential()) determines final behavior.
list.parallelStream().forEach(System.out::println); → Output may be unordered.
list.parallelStream().forEachOrdered(System.out::println);→ Maintains order but kills performance.
A Spliterator splits data into chunks for parallel processing.
Key methods:
trySplit()tryAdvance()estimateSize()
Used internally by collections to divide work efficiently.
long sum = LongStream.rangeClosed(1, 1_000_000)
.parallel()
.sum();List<Integer> results =
ids.parallelStream()
.map(id -> compute(id)) // CPU-heavy
.collect(Collectors.toList());ForkJoinPool pool = new ForkJoinPool(5);
pool.submit(() ->
list.parallelStream().forEach(System.out::println)
).join();Use when tasks are:
- CPU-bound
- Large data sets
- Independent (no shared mutable state)
Thread creation + task splitting overhead > processing time.
Race conditions due to shared mutable state.
Both create parallel streams; only the starting point differs.
ForkJoinPool.commonPool.
Use ForkJoinPool.submit() to wrap the parallel stream.
-
Parallel streams process data concurrently using the Fork–Join framework.
-
Best suited for large, CPU-heavy workloads.
-
Avoid them for I/O tasks, small datasets, and shared mutable state.
-
Order-sensitive operations reduce performance.
-
Understand overhead before using in production systems.