There actually is a trick how to execute a parallel operation in a specific fork-join pool. If you execute it as a task in a fork-join pool, it stays there and does not use the common one.

final int parallelism = 4;
ForkJoinPool forkJoinPool = null;
try {
    forkJoinPool = new ForkJoinPool(parallelism);
    final List<Integer> primes = forkJoinPool.submit(() ->
        // Parallel task here, for example
        IntStream.range(1, 1_000_000).parallel()
                .filter(PrimesPrint::isPrime)
                .boxed().collect(Collectors.toList())
    ).get();
    System.out.println(primes);
} catch (InterruptedException | ExecutionException e) {
    throw new RuntimeException(e);
} finally {
    if (forkJoinPool != null) {
        forkJoinPool.shutdown();
    }
}

The trick is based on ForkJoinTask.fork which specifies: "Arranges to asynchronously execute this task in the pool the current task is running in, if applicable, or using the ForkJoinPool.commonPool() if not inForkJoinPool()"

Answer from Lukáš Křečan on Stack Overflow
Top answer
1 of 16
520

There actually is a trick how to execute a parallel operation in a specific fork-join pool. If you execute it as a task in a fork-join pool, it stays there and does not use the common one.

final int parallelism = 4;
ForkJoinPool forkJoinPool = null;
try {
    forkJoinPool = new ForkJoinPool(parallelism);
    final List<Integer> primes = forkJoinPool.submit(() ->
        // Parallel task here, for example
        IntStream.range(1, 1_000_000).parallel()
                .filter(PrimesPrint::isPrime)
                .boxed().collect(Collectors.toList())
    ).get();
    System.out.println(primes);
} catch (InterruptedException | ExecutionException e) {
    throw new RuntimeException(e);
} finally {
    if (forkJoinPool != null) {
        forkJoinPool.shutdown();
    }
}

The trick is based on ForkJoinTask.fork which specifies: "Arranges to asynchronously execute this task in the pool the current task is running in, if applicable, or using the ForkJoinPool.commonPool() if not inForkJoinPool()"

2 of 16
251

The parallel streams use the default ForkJoinPool.commonPool which by default has one less threads as you have processors, as returned by Runtime.getRuntime().availableProcessors() (This means that parallel streams leave one processor for the calling thread).

For applications that require separate or custom pools, a ForkJoinPool may be constructed with a given target parallelism level; by default, equal to the number of available processors.

This also means if you have nested parallel streams or multiple parallel streams started concurrently, they will all share the same pool. Advantage: you will never use more than the default (number of available processors). Disadvantage: you may not get "all the processors" assigned to each parallel stream you initiate (if you happen to have more than one). (Apparently you can use a ManagedBlocker to circumvent that.)

To change the way parallel streams are executed, you can either

  • submit the parallel stream execution to your own ForkJoinPool: yourFJP.submit(() -> stream.parallel().forEach(soSomething)).get(); or
  • you can change the size of the common pool using system properties: System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "20") for a target parallelism of 20 threads.

Example of the latter on my machine which has 8 processors. If I run the following program:

long start = System.currentTimeMillis();
IntStream s = IntStream.range(0, 20);
//System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "20");
s.parallel().forEach(i -> {
    try { Thread.sleep(100); } catch (Exception ignore) {}
    System.out.print((System.currentTimeMillis() - start) + " ");
});

The output is:

215 216 216 216 216 216 216 216 315 316 316 316 316 316 316 316 415 416 416 416

So you can see that the parallel stream processes 8 items at a time, i.e. it uses 8 threads. However, if I uncomment the commented line, the output is:

215 215 215 215 215 216 216 216 216 216 216 216 216 216 216 216 216 216 216 216

This time, the parallel stream has used 20 threads and all 20 elements in the stream have been processed concurrently.

🌐
Baeldung
baeldung.com › home › java › java concurrency › custom thread pools in java parallel streams
Custom Thread Pools in Java Parallel Streams | Baeldung
December 28, 2023 - In this quick tutorial, we’ll look at one of the biggest limitations of Stream API and see how to make a parallel stream work with a custom ThreadPool instance, alternatively – there’s a library that handles this. Let’s start with a simple example – calling the parallelStream method on any of the Collection types – which will return a possibly parallel Stream:
Discussions

multithreading - How many threads are spawned in parallelStream in Java 8? - Stack Overflow
The parallel.streams API uses the CountedCompleter Class which has some interesting problems. Since the F/J framework does not use a separate object to hold results, long chains may result in an OOME. Also those long chains can sometimes cause a Stack Overflow. The answer to those problems is the use of the Paraquential technique as I pointed out in this article. The other problem is excessive thread ... More on stackoverflow.com
🌐 stackoverflow.com
java - ParallelStream pool size - Stack Overflow
Limiting the threads isn't what you need, you need, like, to use a Semaphore or something to cap the concurrency. ... Since parallel streams are using Fork/Join framework under the hood, to limit the number of treads employed by the stream, you can wrap the stream with a Callable and define ... More on stackoverflow.com
🌐 stackoverflow.com
Is it possible to use virtual threads with parallel streams?
Parallel streams don't have to use the global fork join pool. If you use them in the context of your own fork join pool, that's the one they will use More on reddit.com
🌐 r/java
20
37
March 22, 2023
Custom Thread Pools In Java Parallel Streams
Wish I read this before starting my current project. I was getting crazy memory leaks before I found out you had to shutdown the thread pools. Good article for beginners in parallel processing More on reddit.com
🌐 r/java
6
36
February 14, 2023
🌐
Java Code Geeks
javacodegeeks.com › home › core java
Java 8 Parallel Streams - Custom Thread Pools Examples - Java Code Geeks
April 28, 2021 - If you have only one parallel stream then you can use it with a limited pool count. But, Wait for a java update that parallel stream can take ForkJoinPool as input to limit the number of parallel processes. In this article, You’ve seen how to create parallel streams in java stream api and parallel stream api uses a common share thread pool from ForkJoinPool.
🌐
DEV Community
dev.to › igalhaddad › java-8-parallel-stream-with-threadpool-32kd
Java 8 Parallel Stream with ThreadPool - DEV Community
February 10, 2020 - The creation of the thread pool is done by the utility. We control the number of threads in the pool (int parallelism), the name of the threads in the pool (useful when investigating threads dump), and optionally a timeout limit.
🌐
Medium
medium.com › @daniel.las › performance-of-parallel-java-streams-68988191d9f8
Performance of parallel Java Streams | by Daniel Las | Medium
April 21, 2024 - The difference is that the tested code was executed in 2, 4, 6, 8, 10,12, 14 and 16 threads at the same time on 4 CPU cores virtual machine. Every benchmark was executed against stream of size 1000 and 100 000 to check if the size matters.
🌐
Medium
medium.com › geekculture › pitfalls-of-java-parallel-streams-731fe0c1eb5f
Internals Of Java Parallel Streams | by Thameena S | Geek Culture | Medium
July 1, 2021 - So in total 12 tasks can be executed in parallel here (main + ForkJoinPool), using all 12 CPU cores. Parallel stream is configured to use as many threads as the number of cores in the computer or VM on which the program is running.
🌐
Geeky Hacker
geekyhacker.com › home › java › control threads number in java parallel stream
Control threads number in Java parallel stream - Geeky Hacker
March 30, 2024 - Then we created a custom fork-join pool with a size of five. Finally, we executed the parallelStream statement inside of the submit block. This control threads number in Java parallel stream and enforces the stream to process five records max at the same time.
Find elsewhere
Top answer
1 of 2
144

The Oracle's implementation[1] of parallel stream uses the current thread and in addition to that, if needed, also the threads that compose the default fork join pool ForkJoinPool.commonPool(), which has a default size equal to one less than the number of cores of your CPU.

That default size of the common pool can be changed with this property:

-Djava.util.concurrent.ForkJoinPool.common.parallelism=8

Alternatively, you can use your own pool:

ForkJoinPool myPool = new ForkJoinPool(8);
myPool.submit(() ->
    list.parallelStream().forEach(/* Do Something */);
).get();

Regarding the order, jobs will be executed as soon as a thread is available, in no specific order.

As correctly pointed out by @Holger this is an implementation specific detail (with just one vague reference at the bottom of a document), both approaches will work on Oracle's JVM but are definitely not guaranteed to work on JVMs from other vendors, the property could not exist in a non-Oracle implementation and Streams could not even use a ForkJoinPool internally rendering the alternative based on the behavior of ForkJoinTask.fork completely useless (see here for details on this).

2 of 2
5

While @uraimo is correct, the answer depends on exactly what "Do Something" does. The parallel.streams API uses the CountedCompleter Class which has some interesting problems. Since the F/J framework does not use a separate object to hold results, long chains may result in an OOME. Also those long chains can sometimes cause a Stack Overflow. The answer to those problems is the use of the Paraquential technique as I pointed out in this article.

The other problem is excessive thread creation when using nested parallel forEach.

🌐
TheServerSide
theserverside.com › tip › How-to-use-parallel-streams-in-Java-with-virtual-threads
How to use parallel streams in Java with virtual threads | TheServerSide
Parallel streams utilize the fork/join pool which defaults to the number of available cores. This can help improve performance, but it's limited by the pool size and the fact that this is I/O-bound work. Threads can spend significant time blocked, waiting for file operations to complete.
🌐
Javacodemonk
javacodemonk.com › java-8-parallel-stream-custom-threadpool-48643a91
Java 8 Parallel Stream custom ThreadPool
August 23, 2020 - public class CustomCommonPoolSize ... 192 192 192 192 192 192 192 192 192 192 192 192 192 192 192 192 192 Overall time consumed: 193 ms · Se see that all 20 tasks run in parallel and this the overall time is just 193 ms, even if ...
🌐
Stack Overflow
stackoverflow.com › questions › 74350851 › parallelstream-pool-size
java - ParallelStream pool size - Stack Overflow
Limiting the threads isn't what you need, you need, like, to use a Semaphore or something to cap the concurrency. ... Since parallel streams are using Fork/Join framework under the hood, to limit the number of treads employed by the stream, you can wrap the stream with a Callable and define a ForkJoinPool having the required level of parallelism as described.
🌐
Baeldung
baeldung.com › home › java › java streams › when to use a parallel stream in java
When to Use a Parallel Stream in Java | Baeldung
November 10, 2025 - In a sequential stream, the result of this operation would be 15. But since the reduce operation is handled in parallel, the number five actually gets added up in every worker thread: The actual result might differ depending on the number of threads used in the common fork-join pool.
🌐
Harness
harness.io › blog › service reliability management › fork/join framework vs. parallel streams vs. executorservice: the ultimate fork/join benchmark
Fork/Join Framework vs. Parallel Streams vs. ExecutorService
4 weeks ago - When using the default pool size for Parallel Streams, the same number of cores on the machine (which is eight here), performed almost two seconds worse than the 16 threads version.
🌐
Javaprogramto
javaprogramto.com › 2020 › 05 › java-8-parallel-streams-custom-threadpool.html
Java 8 Parallel Streams - Custom Thread Pools Examples JavaProgramTo.com
May 2, 2021 - If you have only one parallel stream then you can use it with a limited pool count. But, Wait for a java update that parallel stream can take ForkJoinPool as input to limit the number of parallel processes. In this article, You've seen how to create parallel streams in java stream api and parallel stream api uses a common share thread pool from ForkJoinPool.
🌐
Javaspecialists
javaspecialists.eu › archive › Issue311-Virtual-Threads-and-Parallel-Streams.html
[JavaSpecialists 311] - Virtual Threads and Parallel Streams
August 31, 2023 - If I run it with -XX:ActiveProcessorCount=1 or -XX:ActiveProcessorCount=2, or with the common pool parallelism set to 0, then we will see output such as: VirtualThread[#14]/runnable@ForkJoinPool-1-worker-1 VirtualThread[#14]/runnable@ForkJoinPool-1-worker-1 maybe not unmounted time = 14223ms · I hope this helps a bit in case you need to work with large data sets using parallel streams and you want to be sure that your code will also work if being called from a virtual thread. ... P.S. Our Mastering Virtual Threads in Java Course is now available as an in-house course for 10 or more programmers, presented either virtually or in-person.
🌐
LinkedIn
linkedin.com › pulse › choosing-right-thread-pool-java-pavan-pothuganti-clexc
Choosing the Right Thread Pool in Java
December 6, 2023 - Overhead for Small Tasks: For very small tasks or those not benefiting from parallel execution, the overhead of task splitting may outweigh the gains. A CustomThreadPool is a user-defined thread pool in Java, allowing developers to tailor the thread pool configuration based on specific application requirements. Unlike predefined thread pool implementations, a custom thread pool provides flexibility in setting core pool size, maximum pool size, and other parameters.
🌐
Medium
michaelbespalov.medium.com › parallel-stream-pitfalls-and-how-to-avoid-them-91f11808a16c
Parallel stream pitfalls and how to avoid them | by Michael Bespalov | Medium
December 20, 2017 - Running the code above with pool size of 8, we can now verify that threads ForkJoinPool-1-worker-0 to ForkJoinPool-1-worker-7 are used. This way we essentially force the parallel stream to have a ForkJoin as a parent thread, instead of a regular ...
🌐
Codementor
codementor.io › community › controlling parallelism of java 8 collection streams
Controlling Parallelism of Java 8 Collection Streams | Codementor
January 23, 2017 - When we're using collection streams in parallel of Java, there doesn't seem to be any parameter that takes our own thread pool. Some may wonder how many threads certain operation would be given while others may actually believe that we can leave JVM to it because it would know what to do.
🌐
Reddit
reddit.com › r/java › custom thread pools in java parallel streams
r/java on Reddit: Custom Thread Pools In Java Parallel Streams
February 14, 2023 - News, Technical discussions, research papers and assorted things of interest related to the Java programming language NO programming help, NO learning Java related questions, NO installing or downloading Java questions, NO JVM languages - Exclusively Java ... Archived post. New comments cannot be posted and votes cannot be cast. Share ... Wish I read this before starting my current project. I was getting crazy memory leaks before I found out you had to shutdown the thread pools. Good article for beginners in parallel processing