What parallel() actually does
One shared pool, a splitting cost, and a source that may not split at all.
Open this lesson in the learning hubKey points
- Every parallel stream in the JVM shares the same ForkJoinPool.commonPool by default, sized to available processors minus one. One slow parallel stream therefore delays every other one in the process.
- Never run blocking I/O in a parallel stream. Blocking a common-pool thread starves unrelated work across the whole application, including other libraries that use it.
- Splitting is not free. The source must be divisible, the work must be distributed and the results merged - below a few thousand elements of real work, the overhead usually exceeds the gain.
- Sources split very differently. An
ArrayListor array splits perfectly by index; aLinkedListorStream.iteratesplits badly or not at all, so parallel gives no speed-up and pays all the cost. collectinto an ordered collection has to merge in encounter order, which costs.unordered()orgroupingByConcurrentcan be dramatically faster when order genuinely does not matter.- Measure before and after, on realistic data. Parallel streams are one of the few Java features where the intuitive choice is wrong more often than right.
Example
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ForkJoinPool;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class ParallelReality {
static long time(String label, Runnable r) {
long start = System.nanoTime();
r.run();
long ms = (System.nanoTime() - start) / 1_000_000;
System.out.printf(" %-42s %5d ms%n", label, ms);
return ms;
}
static int work(int i) { // a little real CPU work
int h = i;
for (int k = 0; k < 200; k++) { h = h * 31 + k; }
return h;
}
public static void main(String[] args) {
System.out.println("common pool parallelism = "
+ ForkJoinPool.getCommonPoolParallelism()
+ " (shared by EVERY parallel stream in this JVM)");
List<Integer> arrayList = new ArrayList<>();
for (int i = 0; i < 400_000; i++) { arrayList.add(i); }
List<Integer> linkedList = new LinkedList<>(arrayList);
System.out.println();
System.out.println("ArrayList - splits perfectly by index:");
time("sequential", () -> arrayList.stream().mapToInt(ParallelReality::work).sum());
time("parallel", () -> arrayList.parallelStream().mapToInt(ParallelReality::work).sum());
System.out.println();
System.out.println("LinkedList - must be walked to split:");
time("sequential", () -> linkedList.stream().mapToInt(ParallelReality::work).sum());
time("parallel", () -> linkedList.parallelStream().mapToInt(ParallelReality::work).sum());
System.out.println();
System.out.println("Tiny workload - overhead dominates:");
time("sequential", () -> IntStream.range(0, 1_000).sum());
time("parallel", () -> IntStream.range(0, 1_000).parallel().sum());
System.out.println();
System.out.println("Ordered vs unordered collect:");
time("parallel + ordered collect", () ->
arrayList.parallelStream().map(String::valueOf).collect(Collectors.toList()));
time("parallel + groupingByConcurrent", () ->
arrayList.parallelStream()
.collect(Collectors.groupingByConcurrent(i -> i % 16)));
System.out.println();
System.out.println("Rule: parallel needs a splittable source, real CPU work,");
System.out.println(" no blocking, and a measurement proving it helped.");
}
}
Parallel streams share one pool, need a splittable source and real CPU work - and blocking inside one starves the whole JVM.
This is a reading copy. The full lesson — with the visual explainer, the interactive lab and a Run button for the code — lives in the Streams course, and every lesson in it is listed on the Streams contents page.