Java 8 Parallel Streamsのカスタムスレッドプール

Java 8 Parallel Streamsのカスタムスレッドプール

1. 概要

Java 8では、データに対して一括操作を実行する効率的な方法として、Streamsの概念が導入されました。 また、並行性をサポートする環境では、並列Streamsを取得できます。

これらのストリームのパフォーマンスは向上しますが、マルチスレッドのオーバーヘッドが発生します。

このクイックチュートリアルでは、one of the biggest limitations of Stream APIを見て、並列ストリームをカスタムThreadPoolインスタンス、またはthere’s a library that handles thisで機能させる方法を確認します。

2. 並列Stream

簡単な例から始めましょう–CollectionタイプのいずれかでparallelStreamメソッドを呼び出す–これはおそらく並列の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

streamを処理するときに、実際にカスタムThreadPoolを渡すことができます。

次の例では、並列StreamがカスタムThread Poolを使用して、1から1,000,000までの長い値の合計を計算するようにします。

@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);
}

並列処理レベルが4のForkJoinPoolコンストラクターを使用しました。 さまざまな環境に最適な値を決定するにはいくつかの実験が必要ですが、おおよその目安は、CPUのコア数に基づいて数を選択することです。

次に、並列Streamの内容を処理し、reduce呼び出しで合計しました。

この単純な例では、カスタムThread Poolを使用することの完全な有用性を示していない可能性がありますが、一般的なThread Poolを長時間実行されるタスクと結び付けたくない状況では、利点が明らかになります(例: ネットワークソースからのデータの処理)、または共通のThread Poolがアプリケーション内の他のコンポーネントによって使用されています。

4. 結論

カスタムThread Poolを使用して並列Streamを実行する方法について簡単に説明しました。 適切な環境で、並列処理レベルを適切に使用すると、特定の状況でパフォーマンスを向上させることができます。

この記事で参照されている完全なコードサンプルは、over on Githubにあります。