J'ai donc une liste à partir de laquelle j'obtiens un flux parallèle pour remplir une carte, comme suit:
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());
});
Le problème avec ce code est que la carte est imprimée lorsque le processus de "mise en données" est toujours en cours (car c'est parallèle), par conséquent, la carte n'a pas encore reçu tous les éléments de la liste. Bien sûr, dans mon vrai code, il ne s'agit pas seulement d'imprimer la carte; J'utilise une carte pour profiter de O(1) temps de recherche.
Ma question est:
comment faire attendre le thread principal pour que la "mise en données" soit terminée avant l'impression de la carte? J'ai essayé de mettre le "mettre des données" dans un thread t, et faire t.start()
et t.join()
, mais cela n'aide pas.
Peut-être que je ne suis pas censé utiliser le flux parallèle dans ce cas? La liste est longue, et je veux juste profiter du parallélisme pour améliorer l'efficacité.
Avec cette list.parallelStream().forEach
vous violez le side-effects
propriété explicitement indiquée dans la documentation Stream.
Aussi quand vous dites ce code est que la carte est imprimée lorsque le processus de "mise en données" est toujours en cours (car c'est parallèle), ce n'est pas vrai, car forEach
est une opération de terminal et attendra d'être terminée, jusqu'à ce qu'elle puisse passer à un processus la ligne suivante. Vous pourriez être voyant en tant que tel, car vous collectez vers un thread-safe HashMap
et certaines entrées peuvent ne pas être dans cette carte ... Pensez à une autre manière, à quoi se produirait-il si vous mettiez plusieurs entrées de plusieurs threads dans un HashMap
? Eh bien, beaucoup de choses peuvent se casser, comme des entrées manquantes, sur une carte incorrecte ou incohérente, etc.
Bien sûr, le changer en ConcurrentHashMap
fonctionnerait, car il est compatible avec les threads, mais vous violez toujours la propriété des effets secondaires, bien que de manière "sûre".
La bonne chose à faire est de collect
vers un Map
directement sans forEach
:
Map<Integer, TreeNode> map = list.parallelStream()
.collect(Collectors.toMap(
NodeData::getId,
TreeNode::new
));
De cette façon, même pour un traitement parallèle, tout irait bien. Notez juste que vous auriez besoin de lots (dizaines de milliers d'éléments) pour avoir une augmentation mesurable des performances du traitement parallèle.
Les opérations de flux seront bloquées jusqu'à ce qu'elles soient terminées pour les implémentations parallèles et non parallèles.
Donc ce que vous voyez n'est pas the "putting data" process is still going on
- il s'agit très probablement d'une corruption de données, car HashMap
n'est pas threadsafe. Essayez d'utiliser ConcurrentHashMap
à la place.
Je suppose que s'il est possible que le flux soit toujours en cours de traitement, vous pouvez essayer quelque chose comme:
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)
));
Au moins maintenant, vous avez un terminal sur le flux. Vous utiliserez plusieurs threads possibles et le mappage sera certainement terminé.