funciona - linkedlist java
El flujo paralelo desde un HashSet no se ejecuta en paralelo (1)
Tengo colección de elementos que quiero procesar en paralelo. Cuando uso una List
, el paralelismo funciona. Sin embargo, cuando uso un Set
, no se ejecuta en paralelo.
Escribí un ejemplo de código que muestra el problema:
public static void main(String[] args) {
ParallelTest test = new ParallelTest();
List<Integer> list = Arrays.asList(1,2);
Set<Integer> set = new HashSet<>(list);
ForkJoinPool forkJoinPool = new ForkJoinPool(4);
System.out.println("set print");
try {
forkJoinPool.submit(() ->
set.parallelStream().forEach(test::print)
).get();
} catch (Exception e) {
return;
}
System.out.println("/n/nlist print");
try {
forkJoinPool.submit(() ->
list.parallelStream().forEach(test::print)
).get();
} catch (Exception e) {
return;
}
}
private void print(int i){
System.out.println("start: " + i);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
}
System.out.println("end: " + i);
}
Esta es la salida que obtengo en windows 7
set print
start: 1
end: 1
start: 2
end: 2
list print
start: 2
start: 1
end: 1
end: 2
Podemos ver que el primer elemento del Set
tuvo que terminar antes de que se procesara el segundo elemento. Para la List
, el segundo elemento comienza antes de que finalice el primer elemento.
¿Puede decirme qué causa este problema y cómo evitarlo utilizando una colección Set
?
Puedo reproducir el comportamiento que se ve, donde el paralelismo no coincide con el paralelismo del paralelismo del conjunto de bifurcaciones que ha especificado. Después de establecer el paralelismo del conjunto de la bifurcación-unión en 10, y de aumentar el número de elementos en la colección a 50, veo que el paralelismo del flujo basado en la lista aumenta solo a 6, mientras que el paralelismo del flujo basado en conjuntos nunca supera 2.
Sin embargo, tenga en cuenta que esta técnica de enviar una tarea a un grupo de fork-join para ejecutar la secuencia paralela en ese grupo es un "truco" de implementación y no se garantiza que funcione. De hecho, los subprocesos o el conjunto de subprocesos que se utiliza para la ejecución de secuencias paralelas no están especificados . De forma predeterminada, se utiliza el grupo común de fork-join, pero en diferentes entornos, se pueden usar diferentes grupos de subprocesos. (Considere un contenedor dentro de un servidor de aplicaciones).
En la clase java.util.stream.AbstractTask , el campo LEAF_TARGET
determina la cantidad de división que se realiza, que a su vez determina la cantidad de paralelismo que se puede lograr. El valor de este campo se basa en ForkJoinPool.getCommonPoolParallelism()
que, por supuesto, utiliza el paralelismo del grupo común, no el grupo que ejecuta las tareas.
Podría decirse que se trata de un error (consulte el problema JDK-8190974 ), sin embargo, toda esta área no está especificada. Sin embargo, esta área del sistema definitivamente necesita desarrollo, por ejemplo, en términos de políticas de división, la cantidad de paralelismo disponible, el manejo de tareas de bloqueo, entre otras cuestiones. Una versión futura del JDK puede abordar algunos de estos problemas.
Mientras tanto, es posible controlar el paralelismo del grupo común de fork-join mediante el uso de las propiedades del sistema. Si agrega esta línea a su programa,
System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "10");
y ejecuta los flujos en el grupo común (o si los envía a su propio grupo que tiene un nivel suficientemente alto de paralelismo establecido) observará que muchas más tareas se ejecutan en paralelo.
También puede establecer esta propiedad en la línea de comandos usando la opción -D
.
Nuevamente, esto no es un comportamiento garantizado y puede cambiar en el futuro. Pero esta técnica probablemente funcionará para las implementaciones de JDK 8 en el futuro inmediato.