Detailed explanation
A parallel stream is a Java Stream API pipeline that allows elements to be processed concurrently rather than sequentially. You can create one with collection.parallelStream() or switch an existing stream using parallel(). Parallel execution normally uses the shared ForkJoinPool.commonPool(), so it competes with other work using that pool. It is useful when substantial, independent CPU-bound operations over enough elements can offset the costs of splitting, scheduling, and combining work. Parallel does not automatically mean faster.
Under the hood, the source's Spliterator divides the input into partitions. Fork/join tasks process these partitions, and work-stealing helps workers take available tasks from one another. A terminal operation triggers execution; reductions then combine partial results. Arrays and array-backed collections generally split efficiently, while sources that split unevenly can limit speedup. Correct parallel reductions require an associative combining operation and a valid identity. Stream functions should also be non-interfering and usually stateless: they should not modify the source or depend on changing shared data.
Avoid parallel streams for small inputs or cheap operations because coordination overhead can exceed the saved computation. Also avoid them for blocking I/O, such as database calls or HTTP requests: blocked workers reduce the shared pool's capacity, and the stream API does not provide convenient per-pipeline concurrency limits. Prefer explicitly managed executors or suitable asynchronous designs for such workloads. Shared mutable state, such as adding results to an ordinary ArrayList inside forEach(), introduces races; use an appropriate collector instead. Ordering requirements, synchronization, and operations such as ordered limit() or distinct() can also reduce parallel benefits.
In the example, IntStream.range(0, 10_000_000).parallel().filter(n -> n % 2 == 0).count() produces 5,000,000, but its very cheap predicate means even this large input is not proof of a performance gain. Interviewers look for a workload-based decision: consider available CPU cores, per-element cost, source splitting, ordering, and contention from concurrent requests. Benchmark sequential and parallel versions with JMH under representative conditions rather than relying on dataset size or a single elapsed-time measurement.
Key points to remember
- Use parallelStream() or parallel() to enable parallel stream execution.
- Parallel streams normally share the common ForkJoinPool with other tasks.
- Best candidates have substantial, independent CPU work and efficiently splittable inputs.
- Avoid small or cheap workloads, blocking I/O, and unsafe shared mutable state.
- Use associative reductions and benchmark before claiming a speedup.
Common mistakes candidates make
- Assuming a large dataset guarantees that parallel execution will be faster.
- Mutating shared collections inside forEach() instead of using a suitable collector.
- Running blocking database or network calls without considering pool contention and concurrency limits.
Likely follow-up questions
Does a parallel stream preserve element order?
Parallel execution does not necessarily discard the source's encounter order; operations such as toArray() preserve it for ordered streams. However, forEach() does not guarantee processing order, while forEachOrdered() respects encounter order and may reduce parallel benefits.
Why must a parallel reduction be associative?
Parallel execution groups and combines partial results differently from sequential execution. An associative operation gives consistent results regardless of grouping; subtraction is not associative and is therefore unsuitable as a general parallel reduction operator.
Can you assign a dedicated thread pool to a parallel stream?
The Stream API has no parameter for selecting an executor. Submitting the entire terminal operation to a custom ForkJoinPool can use that pool in common JDK implementations, but this is not a Stream API guarantee; explicit task execution is clearer when strict executor control is required.
Interview tip: Define parallel streams, explain their execution model, then contrast a suitable CPU-heavy workload with cases where overhead or contention makes them unsuitable.
