BackendBench
Coffee

Unravel โ€บ Java concepts โ€บ Collections and streams

Java language

Parallel streams

list.parallelStream() splits the work across several threads of the common fork-join pool.

Learn these first

At a glance

Mental model

Hiring extra cooks to chop vegetables faster.

What happens, in order
  1. 1parallelStream() or .parallel() marks the pipeline parallelThe source is split into chunks, each chunk is processed by a thread, and the partial results are combined.
  2. 2It pays off with a large source that splits easily (ArrayList, arrays, ranges), an expensive CPU-bound operation per element, and stateless functions
  3. 3Order-sensitive terminal operations lose their advantage: forEachOrdered keeps the order but serialises the end; forEach does not keep order
  4. 4Lambdas must not modify shared state: adding to a plain ArrayList from a parallel forEach loses elements or throws
Remember
Watch out
Mutating shared collections from a parallel lambdaDoing blocking I/O in a parallel streamAssuming parallel is always faster
Example (highlighted lines explain it)
import java.util.*;import java.util.stream.*;public class Heavy {  static long countPrimes(int max) {    return IntStream.rangeClosed(2, max).parallel().filter(Heavy::isPrime).count();  }  static boolean isPrime(int n) {    for (int i = 2; (long) i * i <= n; i++) if (n % i == 0) return false;    return true;  }  static List<String> upper(List<String> in) { return in.parallelStream().map(String::toUpperCase).collect(Collectors.toList()); }}
  1. line 5 uses parallel, the core of Parallel streams
  2. line 11 uses parallelStream, the core of Parallel streams
Read the full explanation (about 1 min)

list.parallelStream() splits the work across several threads of the common fork-join pool. It can speed up heavy CPU work on big data, and it can also make the program slower or wrong.

A mental model

Hiring extra cooks to chop vegetables faster. That helps with a mountain of vegetables, but for three carrots the hiring takes longer than the chopping, and cooks sharing one cutting board get in each other's way.

How it works

parallelStream() or .parallel() marks the pipeline parallel. The source is split into chunks, each chunk is processed by a thread, and the partial results are combined.

It pays off with a large source that splits easily (ArrayList, arrays, ranges), an expensive CPU-bound operation per element, and stateless functions.

Order-sensitive terminal operations lose their advantage: forEachOrdered keeps the order but serialises the end; forEach does not keep order.

Lambdas must not modify shared state: adding to a plain ArrayList from a parallel forEach loses elements or throws.

Going deeper

All parallel streams in the JVM share one common pool (size = cores - 1). A blocking call (HTTP, database) in one parallel stream can starve every other parallel stream and CompletableFuture in the application.

Measure before using it. Small collections, LinkedList or Stream.iterate sources, boxing and sorted or distinct steps often make it slower. A dedicated ForkJoinPool or normal executors give you control.

Results must not depend on order or on which thread runs: use collect with thread-safe collectors, not forEach plus a shared list.

Under the hood

The Spliterator of the source is split recursively (trySplit) into tasks submitted to ForkJoinPool.commonPool(); results are merged with the collector's combiner. The calling thread also takes part.

Common mistakes

  • Mutating shared collections from a parallel lambda.
  • Doing blocking I/O in a parallel stream.
  • Assuming parallel is always faster.

How Unravel recognises it

When you paste code, Unravel looks for these signs of parallel streams. Spotting them yourself is a good habit when you read unfamiliar code:

Part of a learning path

Related concepts

Frequently asked questions

What is Parallel streams?

list.parallelStream() splits the work across several threads of the common fork-join pool. It can speed up heavy CPU work on big data, and it can also make the program slower or wrong.

What are the most common mistakes with Parallel streams?

Mutating shared collections from a parallel lambda. Doing blocking I/O in a parallel stream. Assuming parallel is always faster.

How can I see Parallel streams in real code?

Paste your own Java into Unravel and it will point out where parallel streams appears, explain how it works at the detail level you choose, and draw the flow of the code around it.