What happens 'under the hood' when using parallelStream()?
sobes.tech AI
Answer from AI
When calling parallelStream() or .parallel() on a stream, operations on stream elements are executed in parallel using a thread pool. By default, ForkJoinPool.commonPool() is used.
The following occurs:
-
Splitting: The original data source (e.g.,
Collection,Array) is divided into several subsets (spliterators). ASpliteratoris an object that can efficiently iterate over the elements of the source and split it into smaller parts. -
Processing: Each subset is processed separately in one of the threads of the
ForkJoinPool. Stream operations (e.g.,map,filter,reduce) are applied to the elements of each subset in parallel. -
Combining: The results of processing from different threads are collected and combined to produce the final result. This step is especially important for terminal operations that aggregate data (e.g.,
reduce,collect).
Fork/Join Framework:
ForkJoinPool is based on the Fork/Join concept:
- Fork: The task is split into smaller subtasks that can be executed in parallel.
- Join: The results of the subtasks are combined to obtain the result of the original task.
ForkJoinPool efficiently manages threads, minimizing overhead for thread creation/destruction and context switching. It uses a work stealing algorithm, where idle threads take tasks from busy threads, ensuring good resource utilization.
Important aspects:
- Not all streams are easily parallelizable. Effectiveness depends on the data source (e.g.,
ArrayListsplits well,LinkedListpoorly) and the type of operations performed (stateless operations parallelize better than stateful ones). - Overhead from parallelization may outweigh benefits for small data volumes or simple operations.
- The result of a parallel stream may be nondeterministic for some operations if they are not associative and commutative (e.g., certain
reduceoperations). - The order of operation execution in a parallel stream is not guaranteed unless ordered operations like
sorted()are used (which may reduce parallelization efficiency).
Example of simple usage:
// Example of using parallelStream for summing
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class ParallelStreamExample {
public static void main(String[] args) {
List<Integer> numbers = IntStream.rangeClosed(1, 1000000)
.boxed()
.collect(Collectors.toList());
long sum = numbers.parallelStream() // Creating a parallel stream
.mapToLong(i -> i) // Mapping to Long for summing
.sum(); // Terminal sum operation
System.out.println("Parallel sum: " + sum);
}
}