Develop code that uses parallel streams, including decomposition operation and reduction operation in streams - Desenvolver código que usa Streams Paralelos, incluindo operação de decomposição e operação de redução em Streams
Streams podem ser sequenciais ou paralelos. Os sequencias foram vistos na seção anterior, enquanto os paralelos serão apresentados nesta seção. Streams paralelos são executados por mais de uma Thread, geralmente uma quantidade igual à quantidade de núcleos do processador onde a aplicação está sendo executada. Apesar disso, nem sempre é útil utilizá-los. Seu ganho real é em Streams com grande volumes de dados. Em um Stream pequeno, transformá-lo em paralelo pode até causar uma perda de performance.
Ao utilizar qualquer tipo de Stream, é recomendável não executar funções lambdas que causem efeitos colaterais, como mudanças no estado de objetos. Em Streams paralelos essa recomendação é ainda mais importante.
-
É possível transformar qualquer Stream em paralelo utilizando o método
parallel.src/org/j6toj8/streams/parallelstreams/Streams_Parallel.javalink:../../../src/org/j6toj8/streams/parallelstreams/Streams_Parallel.java[role=include]
-
É possível criar Streams paralelos diretamente em Coleções através do método
parallelStream.src/org/j6toj8/streams/parallelstreams/Streams_ParallelStream.javalink:../../../src/org/j6toj8/streams/parallelstreams/Streams_ParallelStream.java[role=include]
-
Ao utilizar a operação
forEachem um Stream paralelo, a ordem de execução não é garantida.src/org/j6toj8/streams/parallelstreams/Streams_ParallelForEach.javalink:../../../src/org/j6toj8/streams/parallelstreams/Streams_ParallelForEach.java[role=include]
Saída no consoleSequencial: A B C Paralelo: B C A
O Stream paralelo poderia ter impresso em qualquer ordem, pois não há nenhuma garantia na ordem em que os elementos serão tratados.
-
A operação
forEachOrderedgarante que a ordem será mantida mesmo em Streams paralelos.src/org/j6toj8/streams/parallelstreams/Streams_ParallelForEachOrdered.javalink:../../../src/org/j6toj8/streams/parallelstreams/Streams_ParallelForEachOrdered.java[role=include]
Saída no consoleSequencial: A B C Paralelo: A B C
-
Em coleções com muitos objetos pode haver um ganho considerável de performance.
src/org/j6toj8/streams/parallelstreams/Streams_ParallelPerformance.javalink:../../../src/org/j6toj8/streams/parallelstreams/Streams_ParallelPerformance.java[role=include]
Saída no consoleTempo stream sequencial: 9863 Tempo stream paralelo: 1479
Perceba que na máquina onde o código foi executado, a execução em paralelo demorou apenas 15% do tempo da execução sequencial. Esse não é um teste minucioso, mas mostra o potencial de Streams paralelos.
-
Operações intermediárias que alteram o estado de objetos podem gerar resultados inesperados ao serem executadas em paralelo.
src/org/j6toj8/streams/parallelstreams/Streams_ParallelStatefulOperation.javalink:../../../src/org/j6toj8/streams/parallelstreams/Streams_ParallelStatefulOperation.java[role=include]
Saída no consoleOrdem no forEachOrdered: A B C Ordem na synchronizedList: A C B
Perceba que a ordem foi respeitada na última operação do Stream, o
forEachOrdered, mas não foi respeitada na execução da operação intermediáriamap. Isso ocorre porque essa operação intermediária não precisa seguir a ordem dos itens do stream. -
Diferente da execução em um Stream sequencial, a operação
findAnytraz resultados realmente aleatórios ao ser executada em um Stream paralelo.src/org/j6toj8/streams/parallelstreams/Streams_ParallelFindAny.javalink:../../../src/org/j6toj8/streams/parallelstreams/Streams_ParallelFindAny.java[role=include]
Saída no consolefindAny Sequencial: 7 findAny Paralelo: 9
-
Ao realizar uma operação de reduce não há problema caso o acumulador seja associativo.
src/org/j6toj8/streams/parallelstreams/Streams_ParallelReduceAssociative.javalink:../../../src/org/j6toj8/streams/parallelstreams/Streams_ParallelReduceAssociative.java[role=include]
Saída no console13440 13440
Perceba que o resultado com o Stream sequencial é idêntico ao paralelo. Isso ocorre porque a operação de multiplicação é associativa, ou seja, fazer
(2 x 2) x (3 x 3)é o mesmo que fazer(2 x 2 x 3) x 3, ou até mesmo2 x (2 x 3) x 3. -
Ao realizar uma operação de reduce acumuladores não-associativos irá gerar resultados inesperados.
src/org/j6toj8/streams/parallelstreams/Streams_ParallelReduceNonAssociative.javalink:../../../src/org/j6toj8/streams/parallelstreams/Streams_ParallelReduceNonAssociative.java[role=include]
Saída no console-18 8
Isso ocorre pois a operação de subtração não é associativa, então o resultado pode variar conforme o Stream for "fatiado" para ser executado em paralelo. Ou seja, fazer
1 - 2 - 3 - 4não é o mesmo que fazer(1 - 2) - (3 - 4). -
Para coletar o resultado de um Stream paralelo em um mapa, utilize a operação
toConcurrentMap.src/org/j6toj8/streams/parallelstreams/Streams_ParallelToConcurrentMap.javalink:../../../src/org/j6toj8/streams/parallelstreams/Streams_ParallelToConcurrentMap.java[role=include]
Saída no consoletoMap: {Roseany=7, Amélia=6, Rodrigo=7, Rinaldo=7, Luiz=4} toConcurrentMap: {Amélia=6, Roseany=7, Rodrigo=7, Rinaldo=7, Luiz=4}
Perceba que o resultados das operações pode ser diferente. Ao utilizar o Collector
toConcurrentMapem um Stream paralelo, as operações podem ser executadas em qualquer ordem e não há necessidade de criar múltiplosMap’spara serem combinados posteriormente. Em grandes Streams, isso pode ocasionar em um ganho de performance. -
Para coletar o resultado de um Stream paralelo utilize groupingByConcurrent ao invés de groupingBy.
src/org/j6toj8/streams/parallelstreams/Streams_ParallelGroupingByConcurrent.javalink:../../../src/org/j6toj8/streams/parallelstreams/Streams_ParallelGroupingByConcurrent.java[role=include]
Saída no console{4=[Luiz], 6=[Amélia], 7=[Rinaldo, Rodrigo, Roseany]} {4=[Luiz], 6=[Amélia], 7=[Roseany, Rodrigo, Rinaldo]}
Pelo mesmo motivo do exemplo anterior, a ordem pode variar ao utilizar o
groupingByConcurrent, porém pode haver ganho de performance em grandes Streams paralelos, pois a ordem não é necessariamente seguida e não há necessidade de criar múltiplos mapas.
-
Working with Parallel Streams
Boyarsky, Jeanne; Selikoff, Scott. OCP: Oracle Certified Professional Java SE 8 Programmer II Study Guide (p. 366). Wiley. Edição do Kindle.
-
Parallelism. The Java™ Tutorials.
-
Package java.util.stream. Java Plataform SE 8.
-
Interface Stream<T>. Java Plataform SE 8.