streams
advancedParallel Streams
Switch a stream pipeline to run in parallel on the common ForkJoinPool — for CPU-bound work over large data.
list.parallelStream() splits the work across several threads instead of one. It is one word longer than stream(), which is the problem: it looks free, and it often makes things slower.
A buffet line vs many self-serve stations. Faster if the food is ready and there's enough room — but useless if everyone needs to use the same fryer (a shared resource).
Key Concepts
1
Every parallel stream in your application shares one pool of threads. There is a single pool built into
the JVM, sized to the number of processor cores minus one.
2
So one slow parallel stream delays every other parallel stream in the process, including ones handling
completely unrelated requests. And if your code blocks inside a parallel stream — a database call, an HTTP
request — that thread is unavailable to everyone else while it waits.
3
Splitting the work is not free. Dividing the data, scheduling the pieces, and merging the results all
cost time whether or not the work is worth dividing.
4
As a rough guide, a few thousand items doing trivial work will be slower in parallel than in sequence. The
honest answer to "should this be parallel?" is "measure it", and saying that is stronger than guessing.
5
Some collections split badly. An ArrayList or an array can be divided instantly: take the first half
and the second half by index.
ArrayList
6
A LinkedList cannot. To find the middle you have to walk through it. Stream.iterate is worse — each
item depends on the previous one, so it cannot be divided at all. Reading lines from a file has the same
problem.
LinkedListStream.iterate
7
Running those in parallel pays the splitting cost and gains nothing.
8
The correctness trap is shared state. This looks reasonable and is broken:
9
List<String> out = new ArrayList<>();
list.parallelStream().forEach(out::add); // WRONG10
ArrayList is not built for several threads. You lose items, or get an exception, unpredictably.
ArrayList
11
List<String> out = list.parallelStream().collect(toList()); // right12
collect works because each thread builds its own partial result, and those are merged at the end.
collect
13
Order matters for some operations. findFirst has to respect the original order, so it cannot simply
take whichever answer appears first. If order does not matter to you, findAny is the parallel-friendly
version.
findFirstfindAny
14
And a reduction has to be associative — (a+b)+c must equal a+(b+c). Subtraction is not, so
reduce(0, (a,b) -> a-b) gives different answers depending on how the data was split, silently.
(a+b)+ca+(b+c)reduce(0, (a,b) -> a-b)
15
Inside a web server, the case is weak. The server is already running many requests at once, so all the
cores are busy. Parallelising inside one request takes capacity from other requests, and usually makes the
slowest responses slower rather than faster.
16
The good uses are batch jobs, command-line tools, and genuinely large computations.
17
What the interviewer is probing.1. "When would you use a parallel stream?" Probing: judgement. Stalls: "For big collections."
Moves up: large amounts, real computation per item, a splittable source, no shared state, and not inside
a request thread — then measure.
18
2. "Which threads does it use?" Probing: the shared pool. Stalls: "A thread pool." Moves up: one
pool shared by the whole JVM, sized for computation, so a blocking call starves everything.
19
3. "Why is it slower on a small list?" Probing: fixed costs. Stalls: "It should not be." *Moves
up:* splitting, scheduling and merging cost more than the work itself.
20
4. "forEach adding to an ArrayList. What happens?" Probing: shared state. Stalls: "It is
slower." Moves up: lost items or an exception; use collect.
forEachArrayListcollect
21
5. "Can it change the result?" Probing: ordering and associativity. Stalls: "No." Moves up:
findFirst versus findAny, and a non-associative reduction giving different answers each run.
findFirstfindAny
22
When parallel can pay23
Favourable Unfavourable
------------------------------------------------------------------
N in the hundreds of thousands small collections
CPU-bound per element trivial per-element work
ArrayList, array, IntStream.range LinkedList, Stream.iterate,
BufferedReader.lines()
Stateless, associative ops order-dependent logic
Batch job or CLI inside a web request thread
No IO in the pipeline any blocking call24
The shared pool25
common ForkJoinPool parallelism = Runtime.availableProcessors()-1
-> every parallel stream in the JVM shares it
-> a blocking call inside one starves all the others26
To isolate:
ForkJoinPool pool = new ForkJoinPool(4);
pool.submit(() -> list.parallelStream()...).get();27
Broken versus correct accumulation28
// WRONG -- ArrayList is not thread-safe
List<String> out = new ArrayList<>();
list.parallelStream().forEach(out::add);
// lost elements, or ArrayIndexOutOfBoundsException29
// RIGHT -- per-worker accumulation, merged by the combiner
List<String> out = list.parallelStream().collect(toList());30
Associativity matters31
reduce(0, Integer::sum) associative -> same answer
reduce(0, (a,b) -> a - b) NOT associative -> answer depends
on the split, silently