DEV Community

Cover image for Why Your `.parallelStream()` Might Not Be Parallel at All
Hugo Marques
Hugo Marques

Posted on

4 1 1

Why Your `.parallelStream()` Might Not Be Parallel at All

When we use .parallelStream() in Java, we expect parallelism to magically kick in — blazing fast execution across multiple threads.

But sometimes, even with .parallelStream(), everything still runs in a single thread. No speedup. No parallelism. Just disappointment. 😤


🧪 The Initial Code

I had this pattern:

List<String> users = List.of("alice", "bob");
List<String> products = IntStream.range(0, 10_000)
    .mapToObj(i -> "product-" + i)
    .toList();

List<String> result = users.stream()
    .flatMap(user ->
        products.parallelStream()
            .map(product -> {
                System.out.println("Sequential map: " + user + " - " + product + " on " + Thread.currentThread().getName());
                return user + " → " + product;
            })
    )
    .toList();
Enter fullscreen mode Exit fullscreen mode

Looks fine, right?


🤯 But It Wasn’t Parallel

When I ran it, the output showed every single operation happening in the same thread.

Even though I used .parallelStream()!

Turns out this pattern is broken. Why?


🧠 Why It Doesn’t Work

A much better and simpler explanation than my original article was shared by aleatorio.dev.br on Bluesky 🙌

“The answer lies in the implementation of flatMap, which takes a parallel stream and forcibly calls .sequential() on it, turning it back into a sequential stream.”

So even if you write:

products.parallelStream().map(...)
Enter fullscreen mode Exit fullscreen mode

If that stream is returned inside a flatMap(), Java will implicitly make it sequential again — effectively canceling your parallelism. 😬

I highly recommend checking out aleatorio.dev.br’s full post (I’ll link it in the comments).


✅ The Fix: Flip the Streams

Instead of looping over users and creating parallel streams of products, loop over the products:

List<String> result = products.parallelStream()
    .flatMap(product ->
        users.stream()
            .map(user -> {
                System.out.println("Parallel map: " + user + " - " + product + " on " + Thread.currentThread().getName());
                return user + " → " + product;
            })
    )
    .toList();
Enter fullscreen mode Exit fullscreen mode

Now the outer stream is parallel — and that’s what matters.


🎉 Result

After flipping the stream:

  • Threads used: ✅ yes
  • CPU utilization: ✅ yes
  • Job speed: ✅ improved
  • Logs: showed lots of worker threads doing the job in parallel

It just took one line change to go from single-threaded sadness to full multicore glory. In my particular case, my process time went down from 45 min to 10 min!


✅ TL;DR

  • .parallelStream() only works if the stream itself is parallel
  • Putting .parallelStream() inside a .flatMap() from a sequential stream won’t help
  • Flip the loop: parallelize the outer stream, not the inner one
  • Print Thread.currentThread().getName() to debug what’s really happening

Did this help you? Hit ❤️, drop a comment, or share your own gotchas with Java streams!

Top comments (0)