Quando usamos .parallelStream()
no Java, esperamos que tudo rode em paralelo — rápido, eficiente, multicore.
Mas às vezes, mesmo usando .parallelStream()
, tudo roda em uma única thread. Cadê o paralelo? 😵
🧪 O problema apareceu numa task assíncrona
No meu caso, eu estava executando uma task assíncrona dentro de um ThreadPoolTaskExecutor
(Spring).
Tudo parecia certo: a lógica usava .parallelStream()
e tinha bastante dados pra processar.
List<String> usuarios = List.of("alice", "bob");
List<String> produtos = IntStream.range(0, 10_000)
.mapToObj(i -> "produto-" + i)
.toList();
List<String> resultado = usuarios.stream()
.flatMap(usuario ->
produtos.parallelStream()
.map(produto -> {
System.out.println("Map sequencial: " + usuario + " - " + produto + " na " + Thread.currentThread().getName());
return usuario + " → " + produto;
})
)
.toList();
Mas ao rodar… algo estranho:
📉 Tudo acontecia em uma única thread!
Quando imprimi os nomes das threads, vi que era sempre task-executor-1
.
🧠 Por que esse código não paraleliza nada?
Esse código parece correto, mas tem uma armadilha:
Errata: Uma explicação muito mais simples e melhor foi dada pelo brother
aleatorio.dev.br
no bsky. "A resposta está na implementação do flatmap que pega um stream paralelo e chama o método sequential para forçar a execução sequencial do stream". Eu recomendo ler a thread dele que eu vou postar nos comentários.
- O
usuarios.stream()
é sequencial - O
.flatMap(...)
processa um usuário por vez O.parallelStream()
interno só descreve o trabalho, ele é lazyQuem consome o stream interno é oflatMap
externo- Como o
flatMap
é sequencial, ele consome um stream paralelo por vez - E esse consumo acontece na mesma thread externa
Mesmo com .parallelStream()
, a execução é linear e presa na thread da task.
✅ A solução de verdade: inverter os streams
A real solução é trazer o .parallelStream()
para o nível mais alto — geralmente onde está a maior lista.
Se a lista de produtos
é muito maior que a de usuarios
, o ideal é inverter a ordem:
List<String> resultado = produtos.parallelStream()
.flatMap(produto ->
usuarios.stream()
.map(usuario -> {
System.out.println("Final paralelo: " + usuario + " - " + produto + " na " + Thread.currentThread().getName());
return usuario + " → " + produto;
})
)
.toList();
Agora sim 🎉
Você verá saídas como:
Final paralelo: bob - produto-991 na ForkJoinPool.commonPool-worker-7
Final paralelo: alice - produto-458 na ForkJoinPool.commonPool-worker-1
✅ O paralelismo foi ativado de verdade
✅ CPU sendo usada
✅ Threads trabalhando
✅ Job mais rápido!
🕒 Resultado real
No meu caso, essa simples mudança — inverter os streams — reduziu o tempo de execução de cerca de 45 minutos para apenas 10 minutos.
Uma melhoria absurda com pouquíssimas linhas de código!
✅ TL;DR
- ✅ Use
.parallelStream()
na lista maior - 🚫 Não coloque
.parallelStream()
dentro de.flatMap()
que vem de.stream()
sequencial - 🧠 O problema real é o
flatMap
consumindo os substreams um por vez - 🚀 Inverta a ordem dos streams e traga o paralelismo para fora
- ⏱️ Pode reduzir drasticamente o tempo de execução — no meu caso, de 45min → 10min
Se você curtiu, deixa um ❤️, comenta, ou compartilha!
Tem mais truques de performance em Java que você usa? Bora trocar ideia 👇