AtomicInteger recordNumber = new AtomicInteger();
Files.lines(inputFile.toPath(), StandardCharsets.UTF_8)
.map(record -> new Record(recordNumber.incrementAndGet(), record))
.parallel()
.filter(record -> doSomeOperation())
.findFirst()
Quand j'ai écrit ceci, j'ai supposé que les threads ne seraient engendrés que par l'appel de carte car le parallèle est placé après la carte. Mais certaines lignes du fichier obtenaient des numéros d'enregistrement différents pour chaque exécution.
J'ai lu la documentation officielle des flux Java et quelques sites Web pour comprendre comment les flux fonctionnent sous le capot.
Quelques questions:
Le flux parallèle Java fonctionne sur la base de SplitIterator , qui est implémenté par chaque collection comme ArrayList, LinkedList etc. Lorsque nous construisons un flux parallèle à partir de ces collections, l'itérateur de fractionnement correspondant sera utilisé pour fractionner et itérer la collection. Cela explique pourquoi le parallélisme s'est produit au niveau de la source d'entrée d'origine (lignes de fichier) plutôt qu'au niveau du résultat de la carte (c'est-à-dire Record pojo). Ma compréhension est-elle correcte?
Dans mon cas, l'entrée est un flux d'E / S de fichiers. Quel itérateur divisé sera utilisé?
Peu importe où nous nous situons
parallel()
dans le pipeline. La source d'entrée d'origine sera toujours divisée et les opérations intermédiaires restantes seront appliquées.Dans ce cas, Java ne devrait pas permettre aux utilisateurs de placer des opérations parallèles n'importe où dans le pipeline, sauf à la source d'origine. Parce que cela donne une mauvaise compréhension à ceux qui ne savent pas comment Java Stream fonctionne en interne. Je sais que l'
parallel()
opération aurait été définie pour le type d'objet Stream et donc, cela fonctionne de cette façon. Mais, il est préférable de fournir une autre solution.Dans l'extrait de code ci-dessus, j'essaie d'ajouter un numéro de ligne à chaque enregistrement du fichier d'entrée et il doit donc être commandé. Cependant, je veux appliquer
doSomeOperation()
en parallèle car c'est une logique lourde. La seule façon d'y parvenir est d'écrire mon propre itérateur divisé personnalisé. Est-ce qu'il y a un autre moyen?
Stream
directement dans l' interface et en raison de la mise en cascade agréable, chaque opération est redistribuée Stream
. Imaginez que quelqu'un veuille vous donner Stream
mais a déjà appliqué quelques opérations comme map
celle-ci. En tant qu'utilisateur, vous voulez toujours pouvoir décider de l'exécuter en parallèle ou non. Il doit donc être possible d'appeler parallel()
encore, bien que le flux existe déjà.
flatMap
ou si vous exécutez des méthodes thread-unsafe ou similaires.
Path
est sur le système de fichiers local et que vous utilisez un JDK récent, le séparateur aura une meilleure capacité de traitement parallèle que les multiples de 1024 par lots. Mais le fractionnement équilibré peut même être contre-productif dans certains findFirst
scénarios…
parallel()
n'est rien de plus qu'une demande de modification générale qui est appliquée à l'objet de flux sous-jacent. N'oubliez pas qu'il n'y a qu'un seul flux source si vous n'appliquez pas d'opérations finales au tube, c'est-à-dire tant que rien n'est "exécuté". Cela dit, vous ne faites que remettre en question les choix de conception Java. Ce qui est basé sur l'opinion et nous ne pouvons pas vraiment aider à cela.