Why Java 8 Parallel Streams Can Be Slower Than Simple Loops

A production incident shows that using Java 8 parallel streams for message processing can dramatically reduce throughput, making them slower than a straightforward for‑loop due to the shared ForkJoinPool and thread‑pool sizing issues.

Java Backend Technology
Java Backend Technology
Java Backend Technology
Why Java 8 Parallel Streams Can Be Slower Than Simple Loops

Parallel Stream Speed: Not Always Faster?

Java 8 introduced parallel streams as a convenient way to run multithreaded operations with a single line of code. However, a real production incident showed that using parallel streams in a message‑processing handler caused a dramatic drop in consumption capacity, leading to message backlog.

Parallel Stream Implementation Details

The slowdown originates from the way parallel streams use a common ForkJoinPool. All actions submitted to a parallel stream are processed by this shared pool, regardless of the size of any external thread pool.

void testParallelStream() throws InterruptedException {
    ExecutorService threadPool = new ThreadPoolExecutor(50, 200,
        20, TimeUnit.MINUTES, new ArrayBlockingQueue<>(1000),
        new ThreadFactoryBuilder().setNameFormat("test-parallel-thread").build(),
        new ThreadPoolExecutor.CallerRunsPolicy());

    // for‑loop version
    for (int i = 0; i < ARRAY_LENGTH; i++) {
        CommonExecutor commonExecutor = new CommonExecutor();
        commonExecutor.array = arrays[i];
        threadPool.submit(commonExecutor);
    }
    commonCountDownLatch.await();

    // parallel‑stream version
    for (int i = 0; i < ARRAY_LENGTH; i++) {
        ParallelStreamExecutor parallelStreamExecutor = new ParallelStreamExecutor();
        parallelStreamExecutor.array = arrays[i];
        threadPool.submit(parallelStreamExecutor);
    }
    parallelCountDownLatch.await();
}

The two executors are:

@Data
private static class CommonExecutor implements Runnable {
    private long[] array;
    @Override
    public void run() {
        for (int i = 0; i < array.length; i++) {
            array[i] = i * i;
        }
        commonCountDownLatch.countDown();
    }
}
@Data
private static class ParallelStreamExecutor implements Runnable {
    private long[] array;
    @Override
    public void run() {
        IntStream.range(0, array.length).parallel()
                 .forEach(i -> array[i] = i * i);
        parallelCountDownLatch.countDown();
    }
}

Running both versions ten times on a two‑dimensional array showed that the for‑loop averaged 16 ms while the parallel stream averaged 46 ms, proving the parallel version was slower.

Why Parallel Streams Can Be Slower

Parallel streams submit tasks to the common ForkJoinPool, whose size defaults to the number of available processors minus one. In the example, the parallel version used only seven threads, whereas the manual thread pool provided up to one hundred threads, leading to a large performance gap.

ForkJoinPool diagram
ForkJoinPool diagram

The common pool is created by ForkJoinPool.makeCommonPool(), which reads system properties to configure parallelism, thread factory, and exception handler. Its default parallelism equals the number of CPU cores minus one, and external threads may also help execute subtasks, which can be problematic when global thread‑local state is involved.

Key Pitfalls

All parallel‑stream tasks in the same JVM share the common pool, which can reduce concurrency in a multithreaded environment.

The common pool size is limited and may interact poorly with ThreadLocal variables, causing accidental cleanup of thread‑local data.

Parallel streams are designed for CPU‑bound work; using them for I/O‑heavy operations such as RPC calls can block pool threads and degrade performance.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaperformanceconcurrencyForkJoinPoolParallel Stream
Java Backend Technology
Written by

Java Backend Technology

Focus on Java-related technologies: SSM, Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading. Occasionally cover DevOps tools like Jenkins, Nexus, Docker, and ELK. Also share technical insights from time to time, committed to Java full-stack development!

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.