AtomicInteger recordNumber = new AtomicInteger();
Files.lines(inputFile.toPath(), StandardCharsets.UTF_8)
.map(record -> new Record(recordNumber.incrementAndGet(), record))
.parallel()
.filter(record -> doSomeOperation())
.findFirst()
Als ich dies schrieb, ging ich davon aus, dass die Threads nur beim Kartenaufruf erzeugt werden, da parallel nach der Karte platziert wird. Einige Zeilen in der Datei erhielten jedoch bei jeder Ausführung unterschiedliche Datensatznummern.
Ich habe die offizielle Java-Stream-Dokumentation und einige Websites gelesen , um zu verstehen, wie Streams unter der Haube funktionieren.
Ein paar Fragen:
Der parallele Java-Stream basiert auf SplitIterator , der von jeder Sammlung wie ArrayList, LinkedList usw. implementiert wird. Wenn wir aus diesen Sammlungen einen parallelen Stream erstellen, wird der entsprechende geteilte Iterator zum Teilen und Iterieren der Sammlung verwendet. Dies erklärt, warum Parallelität eher auf der Ebene der ursprünglichen Eingabequelle (Dateizeilen) als auf dem Ergebnis der Karte (dh Record Pojo) auftrat. Ist mein Verständnis richtig?
In meinem Fall ist die Eingabe ein Datei-E / A-Stream. Welcher Split-Iterator wird verwendet?
Es spielt keine Rolle, wo wir
parallel()
in der Pipeline platzieren. Die ursprüngliche Eingabequelle wird immer aufgeteilt und die verbleibenden Zwischenoperationen werden angewendet.In diesem Fall sollte Java Benutzern nicht erlauben, Paralleloperationen an einer beliebigen Stelle in der Pipeline zu platzieren, außer an der ursprünglichen Quelle. Weil es ein falsches Verständnis für diejenigen gibt, die nicht wissen, wie Java Stream intern funktioniert. Ich weiß, dass die
parallel()
Operation für den Stream-Objekttyp definiert worden wäre, und daher funktioniert sie auf diese Weise. Es ist jedoch besser, eine alternative Lösung bereitzustellen.Im obigen Code-Snippet versuche ich, jedem Datensatz in der Eingabedatei eine Zeilennummer hinzuzufügen, und daher sollte sie bestellt werden. Ich möchte mich jedoch
doSomeOperation()
parallel bewerben, da es sich um eine Schwergewichtslogik handelt. Der eine Weg, dies zu erreichen, besteht darin, meinen eigenen angepassten Split-Iterator zu schreiben. Gibt es einen anderen Weg?
Stream
direkt in der Schnittstelle angeboten und aufgrund der schönen Kaskadierung gibt jede Operation wieder Stream
zurück. Stellen Sie sich vor, jemand möchte Ihnen eine geben, hat Stream
aber bereits einige Operationen wie map
diese angewendet . Als Benutzer möchten Sie weiterhin entscheiden können, ob es parallel ausgeführt werden soll oder nicht. Es muss also möglich sein, dass Sie noch anrufen parallel()
, obwohl der Stream bereits vorhanden ist.
flatMap
unsicheren Methoden oder ähnlichem oder ähnlichem dramatisch erhöhen .
Path
im lokalen Dateisystem befindet und Sie ein aktuelles JDK verwenden, verfügt der Spliterator über eine bessere Parallelverarbeitungsfähigkeit als das Stapeln von Vielfachen von 1024. In einigen findFirst
Szenarien kann eine ausgeglichene Aufteilung jedoch sogar kontraproduktiv sein …
parallel()
ist nichts weiter als eine allgemeine Modifikatoranforderung, die auf das zugrunde liegende Stream-Objekt angewendet wird. Denken Sie daran, dass es nur einen Quellstrom gibt, wenn Sie keine endgültigen Operationen auf die Pipe anwenden, dh solange nichts "ausgeführt" wird. Trotzdem hinterfragen Sie im Grunde nur die Auswahl des Java-Designs. Welches ist meinungsbasiert und wir können dabei nicht wirklich helfen.