Pools de encadeamentos personalizados em fluxos paralelos do Java 8
1. Visão geral
Java 8 introduziu o conceito de Streams como uma maneira eficiente de realizar operações em massa nos dados. EStreams paralelo pode ser obtido em ambientes que suportam simultaneidade.
Esses fluxos podem vir com desempenho aprimorado - ao custo de sobrecarga multithread.
Neste tutorial rápido, veremosone of the biggest limitations of Stream APIe veremos como fazer um fluxo paralelo funcionar com uma instânciaThreadPool personalizada, alternativamente -there’s a library that handles this.
2. ParaleloStream
Vamos começar com um exemplo simples - chamando o métodoparallelStream em qualquer um dos tiposCollection - que retornará umStream possivelmente paralelo:
@Test
public void givenList_whenCallingParallelStream_shouldBeParallelStream(){
List aList = new ArrayList<>();
Stream parallelStream = aList.parallelStream();
assertTrue(parallelStream.isParallel());
}
O processamento padrão que ocorre em talStream usa oForkJoinPool.commonPool(),a Thread Pool shared by the entire application.
3. Thread Pool personalizado
Na verdade, podemos passar umThreadPool personalizado ao processarstream.
O exemplo a seguir permite que umStream paralelo use umThread Pool personalizado para calcular a soma dos valores longos de 1 a 1.000.000, inclusive:
@Test
public void giveRangeOfLongs_whenSummedInParallel_shouldBeEqualToExpectedTotal()
throws InterruptedException, ExecutionException {
long firstNum = 1;
long lastNum = 1_000_000;
List aList = LongStream.rangeClosed(firstNum, lastNum).boxed()
.collect(Collectors.toList());
ForkJoinPool customThreadPool = new ForkJoinPool(4);
long actualTotal = customThreadPool.submit(
() -> aList.parallelStream().reduce(0L, Long::sum)).get();
assertEquals((lastNum + firstNum) * lastNum / 2, actualTotal);
}
Usamos o construtorForkJoinPool com um nível de paralelismo de 4. É necessária alguma experimentação para determinar o valor ideal para diferentes ambientes, mas uma boa regra é simplesmente escolher o número com base em quantos núcleos sua CPU possui.
Em seguida, processamos o conteúdo dosStream paralelos, somando-os na chamadareduce.
Este exemplo simples pode não demonstrar a utilidade total de usar umThread Pool personalizado, mas os benefícios se tornam óbvios em situações em que não queremos amarrar oThread Pool comum com tarefas de longa duração (por exemplo processando dados de uma fonte de rede), ou oThread Pool comum está sendo usado por outros componentes dentro do aplicativo.
4. Conclusão
Vimos brevemente como executar umStream paralelo usando umThread Pool personalizado. No ambiente certo e com o uso adequado do nível de paralelismo, podem ser obtidos ganhos de desempenho em determinadas situações.
Os exemplos de código completos mencionados neste artigo podem ser encontradosover on Github.