What parallel() actually does

Streams · lesson 39 of 42 · 7 min read

One shared pool, a splitting cost, and a source that may not split at all.

Open this lesson in the learning hub

Key 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 ArrayList or array splits perfectly by index; a LinkedList or Stream.iterate splits badly or not at all, so parallel gives no speed-up and pays all the cost.
  • collect into an ordered collection has to merge in encounter order, which costs. unordered() or groupingByConcurrent can 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.