AtomicInteger recordNumber = new AtomicInteger();
Files.lines(inputFile.toPath(), StandardCharsets.UTF_8)
.map(record -> new Record(recordNumber.incrementAndGet(), record))
.parallel()
.filter(record -> doSomeOperation())
.findFirst()
Cuando escribí esto, asumí que los hilos se generarán solo en la llamada del mapa, ya que el paralelo se coloca después del mapa. Pero algunas líneas en el archivo obtenían diferentes números de registro para cada ejecución.
Leí la documentación oficial de la transmisión de Java y algunos sitios web para comprender cómo funcionan las transmisiones bajo el capó.
Unas cuantas preguntas:
El flujo paralelo de Java funciona basado en SplitIterator , que se implementa en cada colección como ArrayList, LinkedList, etc. Cuando construimos un flujo paralelo a partir de esas colecciones, el iterador de división correspondiente se usará para dividir e iterar la colección. Esto explica por qué el paralelismo ocurrió en el nivel de la fuente de entrada original (líneas de archivo) en lugar del resultado del mapa (es decir, Grabar pojo). ¿Es correcto mi entendimiento?
En mi caso, la entrada es un archivo IO stream. ¿Qué iterador dividido se usará?
No importa dónde lo ubiquemos
parallel()
en la tubería. La fuente de entrada original siempre se dividirá y se aplicarán las operaciones intermedias restantes.En este caso, Java no debería permitir a los usuarios colocar operaciones paralelas en ningún lugar de la tubería, excepto en la fuente original. Porque, está dando una comprensión errónea para aquellos que no saben cómo funciona Java Stream internamente. Sé que la
parallel()
operación se habría definido para el tipo de objeto Stream y, por lo tanto, funciona de esta manera. Pero, es mejor proporcionar alguna solución alternativa.En el fragmento de código anterior, estoy tratando de agregar un número de línea a cada registro en el archivo de entrada, por lo que debería ordenarse. Sin embargo, quiero aplicar
doSomeOperation()
en paralelo ya que es una lógica pesada. La única forma de lograrlo es escribir mi propio iterador dividido personalizado. ¿Hay alguna otra manera?
Stream
directamente en la interfaz y, debido a la buena conexión en cascada, cada operación devuelve Stream
nuevamente. Imagine que alguien quiere darle una Stream
pero ya ha aplicado un par de operaciones como map
esta. Usted, como usuario, aún quiere poder decidir si desea que se ejecute en paralelo o no. Por lo tanto, debe ser posible llamar parallel()
aún, aunque la transmisión ya existe.
flatMap
o si ejecuta métodos no seguros para subprocesos o similares.
Path
está en el sistema de archivos local y está utilizando un JDK reciente, el spliterator tendrá una mejor capacidad de procesamiento en paralelo que los lotes de múltiplos de 1024. Pero la división equilibrada puede ser incluso contraproducente en algunos findFirst
escenarios ...
parallel()
no es más que una solicitud de modificación general que se aplica al objeto de flujo subyacente. Recuerde que solo hay un flujo fuente si no aplica las operaciones finales a la tubería, es decir, siempre que no se "ejecute" nada. Dicho esto, básicamente solo cuestionas las opciones de diseño de Java. Que se basa en la opinión y realmente no podemos ayudar con eso.