web-dev-qa-db-ja.com

Java並列ストリーム:並列ストリームが終了するまでスレッドを待つ方法は?

したがって、次のように、マップに入力するための並列ストリームを取得するリストがあります。

_Map<Integer, TreeNode> map = new HashMap<>();
List<NodeData> list = some_filled_list;

//Putting data from the list into the map
list.parallelStream().forEach(d -> {
                TreeNode node = new TreeNode(d);
                map.put(node.getId(), node);
            });

//print out map
map.entrySet().stream().forEach(entry -> {
     System.out.println("Processing node with ID = " + entry.getValue().getId());
                });
_

このコードの問題は、「データのパッティング」プロセスがまだ進行しているときにマップが印刷されていることです(これは並列です)。そのため、マップはまだリストからすべての要素を受け取っていません。もちろん、実際のコードでは、マップを出力するだけではありません。 O(1)ルックアップ時間を利用するためにマップを使用します。

私の質問は:

  1. メインスレッドを待機させて、マップが印刷される前に「プットデータ」が終了するようにする方法スレッドt内に「データを置く」ことを試み、t.start()およびt.join()を実行しようとしましたが、それは役に立ちません。

  2. この場合、並列ストリームを使用することは想定されていませんか?リストは長く、並列処理を利用して効率を向上させたいだけです。

10
Simo

このlist.parallelStream().forEachを使用すると、side-effectsプロパティは、Streamのドキュメントで明示的に述べられています。

また、このコードは、「データのパッティング」プロセスがまだ進行しているときにマップが印刷されていることを意味します(それは並列です)forEach端末操作であり、次の行のプロセスに進むことができるまで、終了を待機します。あなたはseeingかもしれません。スレッドセーフでないHashMapに収集していて、一部のエントリがそのマップにない可能性があるためです...他の方法について考えてください。複数のスレッドからの複数のエントリをHashMapに配置すると発生しますか?まあ、多くのものが壊れる可能性があります。たとえば、エントリが見つからない、マップが正しくない/矛盾しているなどです。

もちろん、スレッドセーフであるため、これをConcurrentHashMapに変更しても機能しますが、「安全な」方法ではありますが、副作用プロパティに違反しています。

正しいことは、collectなしで直接MapforEachに直接変換することです。

Map<Integer, TreeNode> map = list.parallelStream()
        .collect(Collectors.toMap(
                NodeData::getId,
                TreeNode::new
        ));

このように、並列処理であっても、すべてがうまくいきます。並列処理によって測定可能なパフォーマンスを向上させるにはlots(数万要素)が必要になることに注意してください。

13
Eugene

ストリーム実装は、並列実装と並列実装ではない両方の処理が完了するまでブロックされます。

したがって、表示されるのはthe "putting data" process is still going onではありません。HashMapはスレッドセーフではないため、おそらくデータの破損だけです。代わりにConcurrentHashMapを使用してみてください。

2
Stadnyk Oleksii

ストリームがまだ処理されている可能性がある場合は、次のようなことを試すことができると思います:

    List<NodeData> list = new ArrayList<>();

    //Putting data from the list into the map
    Map<Integer, TreeNode> map = list.parallelStream()
            .collect(Collectors.toMap(
                    n -> n.getId(),
                    n -> new TreeNode(n)
            ));

少なくとも今、あなたはストリーム上にターミナルを持っています。可能な限り複数のスレッドを使用し、マッピングは確実に完了します。

1
OldCurmudgeon