Пулы пользовательских потоков в параллельных потоках Java 8
1. обзор
В Java 8 была представлена концепция Streams как эффективный способ выполнения массовых операций с данными. И parallelStreams можно получить в средах, поддерживающих параллелизм.
Эти потоки могут иметь улучшенную производительность - за счет многопоточных издержек.
В этом кратком руководстве мы рассмотримone of the biggest limitations of Stream API и увидим, как заставить параллельный поток работать с настраиваемым экземпляромThreadPool, в качестве альтернативы -there’s a library that handles this.
2. ПараллельноStream
Начнем с простого примера - вызов методаparallelStream для любого из типовCollection, который вернет, возможно, параллельныйStream:
@Test
public void givenList_whenCallingParallelStream_shouldBeParallelStream(){
List aList = new ArrayList<>();
Stream parallelStream = aList.parallelStream();
assertTrue(parallelStream.isParallel());
}
Обработка по умолчанию, которая происходит в такомStream, используетForkJoinPool.commonPool(),a Thread Pool shared by the entire application.
3. ПользовательскийThread Pool
Фактически мы можем передать собственныйThreadPool при обработкеstream.
Следующий пример позволяет параллельномуStream использовать настраиваемыйThread Pool для вычисления суммы длинных значений от 1 до 1000000 включительно:
@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);
}
Мы использовали конструкторForkJoinPool с уровнем параллелизма 4. Чтобы определить оптимальное значение для разных сред, требуются некоторые эксперименты, но хорошим практическим правилом является простой выбор числа в зависимости от того, сколько ядер у вашего процессора.
Затем мы обработали содержимое параллельногоStream, суммируя его в вызовеreduce.
Этот простой пример может не продемонстрировать полную полезность использования пользовательскогоThread Pool, но преимущества становятся очевидными в ситуациях, когда мы не хотим связывать общийThread Pool с долгосрочными задачами (например, обработка данных из сетевого источника), или общийThread Pool используется другими компонентами в приложении.
4. Заключение
Мы кратко рассмотрели, как запустить параллельныйStream, используя пользовательскийThread Pool. В правильной среде и при правильном использовании уровня параллелизма прирост производительности может быть достигнут в определенных ситуациях.
Полные примеры кода, упомянутые в этой статье, можно найти вover on Github.