AtomicInteger recordNumber = new AtomicInteger();
Files.lines(inputFile.toPath(), StandardCharsets.UTF_8)
.map(record -> new Record(recordNumber.incrementAndGet(), record))
.parallel()
.filter(record -> doSomeOperation())
.findFirst()
Quando ho scritto questo ho assunto che i thread verranno generati solo dalla chiamata della mappa poiché il parallelo è posizionato dopo la mappa. Ma alcune righe nel file stavano ottenendo numeri di record diversi per ogni esecuzione.
Ho letto la documentazione ufficiale del flusso Java e alcuni siti Web per capire come funzionano i flussi sotto il cofano.
Alcune domande:
Il flusso parallelo Java funziona basato su SplitIterator , che viene implementato da ogni raccolta come ArrayList, LinkedList ecc. Quando costruiamo un flusso parallelo da quelle raccolte, verrà utilizzato l'iteratore di divisione corrispondente per dividere e iterare la raccolta. Questo spiega perché il parallelismo è avvenuto al livello dell'origine di input originale (righe del file) piuttosto che al risultato della mappa (ovvero Record pojo). La mia comprensione è corretta?
Nel mio caso, l'input è un flusso di file IO. Quale iteratore diviso verrà utilizzato?
Non importa dove ci posizioniamo
parallel()
nella pipeline. La sorgente di input originale verrà sempre suddivisa e verranno applicate le operazioni intermedie rimanenti.In questo caso, Java non dovrebbe consentire agli utenti di posizionare operazioni parallele in qualsiasi punto della pipeline, tranne che alla fonte originale. Perché, sta dando una comprensione sbagliata per coloro che non sanno come java stream funzioni internamente. So che l'
parallel()
operazione sarebbe stata definita per il tipo di oggetto Stream e quindi funziona in questo modo. Ma è meglio fornire una soluzione alternativa.Nello snippet di codice sopra riportato, sto cercando di aggiungere un numero di riga a ogni record nel file di input e quindi dovrebbe essere ordinato. Tuttavia, voglio applicare
doSomeOperation()
in parallelo in quanto è una logica pesante. L'unico modo per ottenere è quello di scrivere il mio iteratore diviso personalizzato. C'è un altro modo?
Stream
direttamente nell'interfaccia e, grazie alla buona sequenza, ogni operazione restituisce di Stream
nuovo. Immagina che qualcuno voglia darti una, Stream
ma abbia già applicato un paio di operazioni come map
questa. Come utente, vuoi comunque essere in grado di decidere se eseguirlo in parallelo o meno. Quindi deve essere possibile che tu chiami parallel()
ancora, anche se il flusso esiste già.
flatMap
o se esegui metodi non sicuri o simili.
Path
trova sul filesystem locale e stai usando un JDK recente, lo spliterator avrà una migliore capacità di elaborazione parallela rispetto ai multipli di batch di 1024. Ma la divisione bilanciata può anche essere controproducente in alcuni findFirst
scenari ...
parallel()
non è altro che una richiesta di modifica generale che viene applicata all'oggetto stream sottostante. Ricordare che esiste un solo flusso di origine se non si applicano le operazioni finali alla pipe, vale a dire finché non viene "eseguito" nulla. Detto questo, in pratica stai solo mettendo in discussione le scelte di progettazione di Java. Che è basato sull'opinione e non possiamo davvero aiutarlo.