AtomicInteger recordNumber = new AtomicInteger();
Files.lines(inputFile.toPath(), StandardCharsets.UTF_8)
.map(record -> new Record(recordNumber.incrementAndGet(), record))
.parallel()
.filter(record -> doSomeOperation())
.findFirst()
これを書いたとき、並列はマップの後に配置されるため、スレッドはマップ呼び出しのみで生成されると想定しました。しかし、ファイルのいくつかの行は、実行ごとに異なるレコード番号を取得していました。
公式のJavaストリームドキュメントといくつかのWebサイトを読んで、ストリームが内部でどのように機能するかを理解しました。
いくつかの質問:
Java並列ストリームは、ArrayList、LinkedListなどのすべてのコレクションによって実装されるSplitIteratorに基づいて機能します。これらのコレクションから並列ストリームを構築する場合、対応する分割イテレータを使用して、コレクションを分割および反復します。これは、マップの結果(つまり、レコードポジョ)ではなく、元の入力ソース(ファイル行)レベルで並列処理が発生した理由を説明しています。私の理解は正しいですか?
私の場合、入力はファイルIOストリームです。使用される分割イテレーターはどれですか?
parallel()
パイプラインのどこに配置してもかまいません。元の入力ソースは常に分割され、残りの中間操作が適用されます。この場合、Javaは、ユーザーが元のソース以外のパイプラインの任意の場所に並列操作を配置できないようにする必要があります。それは、Javaストリームが内部でどのように機能するかを知らない人たちに間違った理解を与えているからです。私が知っている
parallel()
操作はStreamオブジェクトタイプのために、それがこのように働いている、ように定義されていたであろう。ただし、代替ソリューションを提供することをお勧めします。上記のコードスニペットでは、入力ファイルのすべてのレコードに行番号を追加しようとしているので、順序付けする必要があります。ただし、
doSomeOperation()
ヘビーウェイトのロジックなので並行して適用したい。達成する1つの方法は、独自にカスタマイズした分割イテレーターを作成することです。他に方法はありますか?
Stream
インターフェースで直接提供されますStream
。また、カスケードが適切に行われているため、すべての操作が再び返されます。誰かがあなたに与えたいと思っているStream
が、すでにmap
それにいくつかの操作を適用していると想像してください。ユーザーとして、並行して実行するかどうかを決定できるようにしたいと考えています。したがってparallel()
、ストリームはすでに存在していますが、あなたが静止して呼び出すことが可能でなければなりません。
flatMap
たり、スレッドに対して安全でないメソッドなどを実行したりするような、エッジケースが発生することを理解しています。
Path
ローカルファイルシステム上にあり、最近のJDKを使用している、spliteratorは、1024の倍数をバッチ処理よりも優れた並列処理能力を持っていますが、バランスの取れた分割は、いくつかの中でも、逆効果かもしれfindFirst
...シナリオ
parallel()
基になるストリームオブジェクトに適用される一般的な修飾子リクエストにすぎません。パイプに最終操作を適用しない場合、つまり何も「実行」されない限り、ソースストリームは1つしかないことに注意してください。そうは言っても、基本的にはJavaデザインの選択に疑問を投げかけるだけです。これは意見に基づくものであり、私たちは本当にそれを助けることはできません。