Develop code that uses parallel streams, including decomposition operation and reduction operation in streams
Streams can be sequential or parallel. Sequential was seen in the previous section, while parallels will be presented in this section. Parallel Streams are executed by more than one Thread, usually equal to the number of processor cores where the application is running. Nevertheless, it is not always useful to use them. Your actual gain is Streams using large volumes of data. In a small Stream, turning it parallel can even cause a loss of performance.
When using any kind Stream, it is recommended not to perform Lambda functions that cause side effects, such as changes in the state of objects. In parallel Streams, this recommendation is even more important.
-
You can make any Stream parallel using the
parallelmethod.src/org/j6toj8/streams/parallelstreams/Streams_Parallel.javalink:../../../src/org/j6toj8/streams/parallelstreams/Streams_Parallel.java[role=include]
-
You can create parallel Streams directly in Collections through the
parallelStreammethod.src/org/j6toj8/streams/parallelstreams/Streams_ParallelStream.javalink:../../../src/org/j6toj8/streams/parallelstreams/Streams_ParallelStream.java[role=include]
-
When using the
forEachoperation on a parallel Stream, the order of execution is not guaranteed.src/org/j6toj8/streams/parallelstreams/Streams_ParallelForEach.javalink:../../../src/org/j6toj8/streams/parallelstreams/Streams_ParallelForEach.java[role=include]
console outputSequential: A B C Parallel: B C A
The parallel Stream could have printed in any order as there is no guarantee in the order in which the elements will be handled.
-
The
forEachOrderedoperation ensures that order will be maintained even in parallel Streams.src/org/j6toj8/streams/parallelstreams/Streams_ParallelForEachOrdered.javalink:../../../src/org/j6toj8/streams/parallelstreams/Streams_ParallelForEachOrdered.java[role=include]
console outputSequential: A B C Parallel: A B C
-
In collections with many objects, there can be a considerable performance gain.
src/org/j6toj8/streams/parallelstreams/Streams_ParallelPerformance.javalink:../../../src/org/j6toj8/streams/parallelstreams/Streams_ParallelPerformance.java[role=include]
console outputSequential Stream time: 9863 Parallel Stream time: 1479
Note that on the machine where the code was executed, parallel execution took only 15% of the sequential execution time. This is not a thorough test, but it does show the potential of parallel Streams.
-
Intermediate operations that change the state of objects can produce unexpected results when executed in parallel.
src/org/j6toj8/streams/parallelstreams/Streams_ParallelStatefulOperation.javalink:../../../src/org/j6toj8/streams/parallelstreams/Streams_ParallelStatefulOperation.java[role=include]
console outputOrder on forEachOrdered: A B C Order on synchronizedList: A C B
Note that the order was respected in the last Stream operation,
forEachOrdered, but was not respected when performing themapintermediate operation. This is because this intermediate operation does not have to follow the order of stream items. -
Unlike running in a sequential Stream, the
findAnyoperation brings really random results when executed in a parallel Stream.src/org/j6toj8/streams/parallelstreams/Streams_ParallelFindAny.javalink:../../../src/org/j6toj8/streams/parallelstreams/Streams_ParallelFindAny.java[role=include]
console outputfindAny Sequential: 7 findAny Parallel: 9
-
When performing a reduce operation there is no problem if the accumulator is associative.
src/org/j6toj8/streams/parallelstreams/Streams_ParallelReduceAssociative.javalink:../../../src/org/j6toj8/streams/parallelstreams/Streams_ParallelReduceAssociative.java[role=include]
console output13440 13440
Note that the result with sequential Stream is identical to parallel. This is because the multiplication operation is associative, i.e., doing
(2 x 2) x (3 x 3)is the same as doing(2 x 2 x 3) x 3, or even2 x (2x3) x3. -
Performing a reduce non-associative accumulator operation will produce unexpected results.
src/org/j6toj8/streams/parallelstreams/Streams_ParallelReduceNonAssociative.javalink:../../../src/org/j6toj8/streams/parallelstreams/Streams_ParallelReduceNonAssociative.java[role=include]
console output-18 8
This is because the subtraction operation is not associative, so the result may vary as Stream is "sliced" to run in parallel. That is, doing
1 - 2 - 3 - 4is not the same as doing(1 - 2) - (3 - 4). -
To collect the result of a parallel Stream on a map, use the
toConcurrentMapoperation.src/org/j6toj8/streams/parallelstreams/Streams_ParallelToConcurrentMap.javalink:../../../src/org/j6toj8/streams/parallelstreams/Streams_ParallelToConcurrentMap.java[role=include]
console outputtoMap: {Roseany=7, Amélia=6, Rodrigo=7, Rinaldo=7, Luiz=4} toConcurrentMap: {Amélia=6, Roseany=7, Rodrigo=7, Rinaldo=7, Luiz=4}
Note that the results of operations may differ. By using Collector
toConcurrentMapin a parallel Stream, operations can be performed in any order and there is no need to create multipleMap’sto be combined later. In large Streams, this can lead to a performance gain. -
To collect the result of a parallel stream use groupingByConcurrent instead of groupingBy.
src/org/j6toj8/streams/parallelstreams/Streams_ParallelGroupingByConcurrent.javalink:../../../src/org/j6toj8/streams/parallelstreams/Streams_ParallelGroupingByConcurrent.java[role=include]
console output{4=[Luiz], 6=[Amélia], 7=[Rinaldo, Rodrigo, Roseany]} {4=[Luiz], 6=[Amélia], 7=[Roseany, Rodrigo, Rinaldo]}
For the same reason as in the previous example, the order may vary when using
groupingByConcurrent, but there may be performance gains on large parallel Streams as the order is not necessarily followed and there is no need to create multiple maps.
-
Working with Parallel Streams
Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer II Study Guide (p. 366). Wiley. Kindle Edition.
-
Parallelism. The Java™ Tutorials.
-
Package java.util.stream. Java Plataform SE 8.
-
Interface Stream<T>. Java Plataform SE 8.