0Pricing
Java Academy · レッスン

並列ストリーム:性能と注意点

並列ストリームを有効にし、共通スレッドプールの仕組みを理解して、共有可変状態によるバグを避けます。

「並列ストリーム:性能と注意点」はCoddyKit上の無料Java Academyレッスンです。 これはレッスン2/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはJava Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Java Academyコースには全4レッスンが含まれています。

並列ストリームを有効にする

任意のストリームで.parallel()を呼び出すか、Collection.parallelStream()を使用します。ストリームの操作は共通のForkJoinPool(デフォルトではCPUコア数 - 1個のスレッド)で実行されます。

long count = list.parallelStream()
    .filter(n -> n % 2 == 0)
    .count();
System.out.println(count);

並列処理が効果を発揮する場合

並列ストリームが効果的なのは、データセットが大きく(100,000要素以上)、各要素の処理に計算コストがかかり、パイプラインがステートレスで順序に依存しない場合です。

// Good candidate: CPU-heavy computation on large dataset
long sum = LongStream.rangeClosed(1, 10_000_000)
    .parallel()
    .filter(n -> isPrime(n))
    .sum();
System.out.println(sum);

並列ストリームを使用してはいけない場合

小さなコレクション、I/Oバウンドの処理(ForkJoinPoolのスレッドをブロックします)、ステートフルな操作(sorting、distinct)、または順序が重要で復元にコストがかかる場合は、並列ストリームを避けてください。

// Bad: I/O bound — blocking ForkJoinPool threads starves other tasks
List<String> result = urls.parallelStream()
    .map(url -> httpGet(url)) // BLOCKS the common pool
    .collect(Collectors.toList());

共有可変状態のバグ

並列ストリームでは、複数のスレッドが同時に操作を実行します。共有された可変コレクションを変更すると、データ競合が発生し、結果が不正になります。

// RACE CONDITION — never do this:
List<Integer> results = new ArrayList<>(); // not thread-safe
numbers.parallelStream().forEach(n -> results.add(n)); // corrupts list!
// Fix:
List<Integer> safe = numbers.parallelStream().collect(Collectors.toList());

ステートフルな操作: sorted と distinct

sorted()とdistinct()は、出力を生成する前にすべての要素を確認する必要があるため、並列性が制限され、並列処理が逐次処理より遅くなることがよくあります。

// sorted() forces collect-all, then sort — parallel overhead usually not worth it:
list.parallelStream().sorted().collect(Collectors.toList());

順序に依存する操作

findFirst()とforEachOrdered()は並列ストリームでも遭遇順序を維持するため、同期処理のコストが増加します。順序が重要でない場合は、findAny()またはforEach()を使用してください。

// Faster in parallel (order-insensitive):
Optional<Integer> any = list.parallelStream().filter(n -> n > 10).findAny();
// Slower in parallel (must preserve order):
Optional<Integer> first = list.parallelStream().filter(n -> n > 10).findFirst();

スレッドプールのサイズを選択する

共通のForkJoinPoolは、Runtime.getRuntime().availableProcessors() - 1個のスレッドを使用します。ForkJoinPool.invoke()の呼び出し内で並列ストリームをサブミットすると、カスタムプールで実行できます。

ForkJoinPool custom = new ForkJoinPool(8);
long result = custom.submit(
    () -> LongStream.rangeClosed(1, 1_000_000).parallel().sum()
).get();

並列処理と逐次処理のベンチマーク

現実的なデータサイズを使い、必ずJMHでベンチマークしてください。並列処理のオーバーヘッド(分割、スレッド間の調整、結合)が正当化されるのは、計算時間がオーバーヘッドを大幅に上回る場合だけです。

@Benchmark
public long sequential() { return LongStream.rangeClosed(1,1_000_000).sum(); }
@Benchmark
public long parallel()   { return LongStream.rangeClosed(1,1_000_000).parallel().sum(); }

並列処理での縮約操作

reduce()とcollect()は、操作が結合的で、単位元が正しく設定されていれば、並列処理でも正しく動作するように設計されています。

// Associative reduce — safe in parallel:
int sum = list.parallelStream().reduce(0, Integer::sum);
// Non-associative: subtraction — NOT safe in parallel:
int bad = list.parallelStream().reduce(0, (a, b) -> a - b); // wrong result!

分割可能性が重要

並列ストリームは、Spliteratorを使ってデータソースを分割します。ArrayListと配列はO(1)で分割できますが、LinkedListとHashSetは分割の効率が悪く、並列処理の効率が低下します。

まとめ: 並列ストリームのチェックリスト

並列処理を使用する前に確認しましょう: (1) 大きなデータセット、(2) CPUバウンドの操作、(3) 共有された可変状態がない、(4) 順序に依存しない、(5) 分割可能なソース(配列またはArrayList)。迷った場合はベンチマークしてください。

確認問題

並列ストリームのforEachで、スレッドセーフでないコレクションに要素を追加するとどうなりますか。

振り返り

並列ストリームはForkJoinPoolを使用します。大規模でCPUバウンド、順序に依存せず、ステートレスなパイプラインに適しています。共有状態を変更してはいけません。採用を決める前にベンチマークしてください。小規模なデータでは、並列処理のほうが遅くなることがよくあります。

よくある質問

「並列ストリーム:性能と注意点」レッスンは無料ですか?

はい。「並列ストリーム:性能と注意点」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Java Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Java Academyコースには全4レッスンが含まれています。

「並列ストリーム:性能と注意点」で何を学びますか?

並列ストリームを有効にし、共通スレッドプールの仕組みを理解して、共有可変状態によるバグを避けます。 ブラウザで直接実行するハンズオンコードでJava Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Java Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのJava Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/4です。

「並列ストリーム:性能と注意点」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このJava Academyレッスンでコードを書いて実行できますか?

はい。すべてのJava Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. ネストしたコレクションに対するflatMap
  2. 並列ストリーム:性能と注意点
  3. Spliterator:並列処理のための分割
  4. iterateとgenerateによる無限ストリーム
← Java Academyに戻る