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.

🌐
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.
Discussions

java - How to limit number of parallel executions in ParallelStream? - Stack Overflow
If you really wish to use a custom thread pool within the parallel streams, please, refer to this question: Custom thread pool in Java 8 parallel stream. More on stackoverflow.com
🌐 stackoverflow.com
multithreading - How many threads are spawned in parallelStream in Java 8? - Stack Overflow
Maybe worth adding that if the number of tasks is small enough the parallel stream may actually run in the main thread. More on stackoverflow.com
🌐 stackoverflow.com
Given a large number of tasks, are threads or parallel streams more performant?
Parallel streams are implemented using internal threads. So...not sure if it would make any difference regarding performance. More on reddit.com
🌐 r/java
15
14
July 27, 2015
How do I limit number of threads in parallel calculations to avoid OutOfMemoryException?
Hello, I have some action in Java (8) which is performed in parallel threads over a uniform list of data. On one hand I need a single thread for each piece of data, on the other hand, I don't know... More on researchgate.net
🌐 researchgate.net
17
0
September 11, 2014
🌐
DEV Community
dev.to › igalhaddad › java-8-parallel-stream-with-threadpool-32kd
Java 8 Parallel Stream with ThreadPool - DEV Community
February 10, 2020 - The handy ThreadExecutor utility by Igal Haddad offers an elegant solution to this, allowing developers to specify the number of threads, custom thread names, and timeouts, making it ideal for scenarios that require fine-tuned parallel execution. For example, in performance-heavy applications like game cheat engines—think Easy Victory Game Cheats Free—having precise control over thread execution can improve processing speed and debugging clarity.
🌐
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 - We used the ForkJoinPool constructor with a parallelism level of 4. Some experimentation is required to determine the optimal value for different environments, but a good rule of thumb is simply choosing the number based on how many cores your CPU has. Next, we processed the content of the parallel Stream, summing them up in the reduce call. This simple example may not demonstrate the full usefulness of using a custom thread pool, but the benefits become obvious in situations where we do not want to tie-up the common thread pool with long-running tasks – such as processing data from a network source – or the common thread pool is being used by other components within the application.
🌐
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 - And also the parallelism is not controllable. In this article, we cover how to control threads number in Java parallel stream by using a custom fork-join pool. The idea is to create a custom fork-join pool with a desirable number of threads and execute the parallel stream within it.
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.

🌐
Medium
medium.com › @sum98kumar › java-interview-questions-9cb306e392d3
Java Interview Questions.. Q. How many thread will open for… | by Suman Kumar | Medium
February 15, 2024 - It typically creates as many worker threads as there are processors available to the Java Virtual Machine (JVM). This means that the number of threads created for a parallel stream is usually equal to the number of available processor cores.
Find elsewhere
🌐
Java Code Geeks
javacodegeeks.com › home › core java
Java 8 Parallel Streams - Custom Thread Pools Examples - Java Code Geeks
April 28, 2021 - In fact, this also creating a parallel stream inside ForkJoinPool which again internally consumes threads from a common pool of ForkJoinPool area. So, If you are running multiple parallel streams then do not use this Steam api parallel method as this might slow other streams give the results in more time. Here, we have taken the pool count as 5 but you can change it as per your CPU configuration. If you have more then you can fine-tune based on the other tasks. If you have only one parallel stream then you can use it with a limited pool count.
🌐
Under the hood
kamilszymanski.github.io › controlling-parallelism-level-of-java-parallel-streams
Controlling parallelism level of Java parallel streams - Under the hood
October 16, 2017 - private static final int PARALLELISM_LEVEL = 8; Set<FormattedMessage> formatMessages(Set<RawMessage> messages) { ForkJoinPool forkJoinPool = new ForkJoinPool(PARALLELISM_LEVEL); try { return forkJoinPool.submit(() -> formatMessagesInParallel(messages)) .get(); } catch (InterruptedException | ExecutionException e) { // handle exceptions } finally { forkJoinPool.shutdown(); } } private Set<FormattedMessage> formatMessagesInParallel(Set<RawMessage> messages) { return messages.stream() .parallel() .map(MessageFormatter::format) .collect(toSet()); } In this example we’re interested only in the parallelism level of the ForkJoinPool though we can also control ThreadFactory and UncaughtExceptionHandler if needed.
🌐
Java2Blog
java2blog.com › home › core java › java 8 › java parallel stream
Java Parallel Stream - Java2Blog
January 26, 2021 - To solve this issue, you can create own thread pool while processing the stream. ... This will create ForkJoinPool with target parallelism level. If you don’t pass parallelism, it will equal to the number of processors by default.
🌐
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.
🌐
Stack Abuse
stackabuse.com › java-8-streams-guide-to-parallel-streaming-with-parallel
Java 8 Streams: Definitive Guide to Parallel Streaming with parallel()
October 24, 2023 - That being said - let's summarize the best practices: Associativity: expect results to come without following any order ... Only use parallelism when the number of elements is very huge. For example, with a stream of int elements which are less than 10,000, prefer serial to parallel execution.
🌐
Medium
medium.com › @michaelbespalov › 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 ...
🌐
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
July 3, 2024 - 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.
🌐
JRebel
jrebel.com › blog › parallel-java-streams
Take Caution Using Java Parallel Streams | JRebel by Perforce
July 3, 2014 - And given the current ForkJoin pool implementation, which doesn’t compensate workers that are stuck waiting with other freshly spawned workers, at some point of time all the threads in the ForkJoinPool.common() will be exhausted. Which means next time you call the query method, above, at the same time with any other parallel stream processing, the performance of the second task will suffer!
🌐
Glassthought
glassthought.com › notes › lc2wo9z3twpqypqncncbgzd
Parallel Streams with Custom Thread Pool - GlassThought.com
December 21, 2023 - To control the number of threads used by parallel streams in Java, you need to use a custom ForkJoinPool. This allows you to specify the exact number of threads you want the parallel stream operations to be executed on.
🌐
Coderanch
coderanch.com › t › 657360 › java › Specific-Number-Parallel-Streams
Specific Number of Parallel Streams (Features new in Java 8 forum at Coderanch)
[OCP 21 book] | [OCP 17 book] | ... and part 2 ... Jeanne Boyarsky wrote: As a point of interest, the Stream API sets off two times the number of threads in parallel than you have cores....