Пулы пользовательских потоков в параллельных потоках Java 8

Пулы пользовательских потоков в параллельных потоках 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.