Parallel stream von einem HashSet nicht parallel laufen
Habe ich Sammlung von Elementen, die möchte ich parallel verarbeiten. Wenn ich mit einem List
, Parallelität funktioniert. Jedoch, wenn ich mit einem Set
es nicht parallel laufen lassen.
Schrieb ich ein Codebeispiel, das veranschaulicht das problem:
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);
}
Dies ist die Ausgabe, die ich auf windows 7
set print
start: 1
end: 1
start: 2
end: 2
list print
start: 2
start: 1
end: 1
end: 2
Können wir sehen, dass das erste element aus der Set
hatte, zu beenden, bevor das zweite element verarbeitet. Für die List
, das zweite element beginnt, bevor das erste element beendet.
Können Sie mir sagen, was dieses Problem verursacht und wie man es vermeiden mit einem Set
Sammlung?
- versuchen Sie es mit mehr als zwei Elementen, wie 10. Elemente oder so etwas. die Ergebnisse mit 2 ist zu vage
- Wenn Sie versuchen mit 10 Sie nicht immer noch parallel alle set-Elemente. Und ich muss alle Elemente parallel.
- Jeder Weg, dies ist der Ausgang für 10 (mit Schwimmbad von 10 Vollzieher) Elemente festlegen drucken start: 8 gestartet: 0 gestartet: 4 start: 6 Beginn: 2 Ende: 2 end: 6 end: 4 end: 0 start: 1 Ende: 8 start: 9 start: 5 start: 7 start: 3 Ende: 3 end: 5 Ende: 9 end: 7: 1 Liste drucken start: 7 start: 3 start: 0 start: 6 start: 9 start: 8 start: 5 start: 4 start: 2 start: 1 end: 0 end: 6 Ende: 7 Ende: 9 Ende: 2 end: 3 end: 8-Ende: 5: 1 end: 4 Nicht alle set-Elemente parallel laufen
Du musst angemeldet sein, um einen Kommentar abzugeben.
Kann ich das Verhalten reproduzieren, sehen Sie, wo die Parallelität der nicht mit der Parallelität der fork-join-pool Parallelität, die Sie angegeben haben. Nach der Einstellung der fork-join-pool Parallelität zu 10, und die Erhöhung der Anzahl der Elemente in der Auflistung, 50, sehe ich die Parallelität der Liste-basiert stream steigt nur bis 6, in der Erwägung, dass die Parallelität des set-based-stream wird nie über 2.
Beachten Sie jedoch, dass dieses Verfahren von der Einreichung einer Aufgabe zu einem fork-join-pool, die zum ausführen der parallele Datenstrom in diesem pool ist eine Implementierung "trick" und ist nicht garantiert zu arbeiten. Ja, die threads oder thread-pool, der verwendet wird, für die Ausführung von parallelen streams ist unbekannter. Standardmäßig ist die gemeinsame fork-join-pool ist gebraucht, aber in verschiedenen Umgebungen, verschiedene thread-pools könnten am Ende verwendet wird. (Betrachten Sie einen container innerhalb eines Anwendungsservers.)
In der java.util.stream.AbstractTask Klasse, die
LEAF_TARGET
Feld bestimmt die Stärke der Aufspaltung, das getan wird, was wiederum bestimmt den Grad der Parallelität erreicht werden kann. Der Wert dieses Feldes basiert aufForkJoinPool.getCommonPoolParallelism()
was natürlich nutzt die Parallelität von der gemeinsamen pool, kein pool, was passiert nach dem ausführen des tasks.Wohl das ein bug ist (siehe OpenJDK Problem JDK-8190974), jedoch ist dieser gesamte Bereich ist nicht spezifiziert sowieso. Jedoch, in diesem Bereich das system benötigt auf jeden Fall die Entwicklung, zum Beispiel in Bezug auf die Aufteilung Politik, die Menge der Parallelität verfügbar sind, ist der Umgang mit blockierenden Aufgaben, unter anderem Fragen. Eine zukünftige Version des JDK kann auf einige dieser Fragen.
Mittlerweile ist es möglich, die Kontrolle der Parallelität der gemeinsamen fork-join-pool, durch den Einsatz von system-Eigenschaften. Wenn Sie fügen Sie diese Zeile in Ihrem Programm,
und Sie führen die Bäche in den gemeinsamen pool (oder, wenn Sie uns diese zum eigenen pool, der eine ausreichend hohe Parallelität) werden Sie feststellen, dass es viele weitere Aufgaben, die parallel ausgeführt werden.
Können Sie auch setzen Sie diese Eigenschaft auf der Befehlszeile mit der
-D
option.Wieder, das ist garantiert nicht Verhalten, und es kann sich in Zukunft ändern. Aber diese Technik wird wahrscheinlich funktionieren, für JDK 8-Implementierungen für die absehbare Zukunft.
UPDATE 2019-06-12: Der Fehler JDK-8190974 wurde behoben, JDK 10, und das Update wurde zurück portiert auf eine anstehende JDK-8u release (8u222).
ForkJoinPool
(Standard oder andere) oder etwas anderes?ForkJoinPool
funktioniert - es hat so etwas wie eine back-pressure-Mechanismus gegen unnötige thread-proliferation. Ich habe eine Antwort auf die Frage, die du verlinkt hast, erklärt das Verhalten.AbstractTask
Verhalten und meine Antwort über dieForkJoinPool
zurück-der Druck der Ursache ist falsch. Dank Euch Jungs für Lager mit mir - werde ich update meine Antwort entsprechend. Für @SotiriosDelimanolis Kommentar hier, unabhängig davon, ob dies als Fehler klassifiziert, und ob die Verwendung eines nicht-Standard-pool-sollte unterstützt werden, dies zu beheben, müssen möglicherweise weitere wesentliche änderungen, wie jetzt der stream nicht wissen, über die Parallelität der Ebene der pool, in dem Sie ausgeführt wird.System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "10");
aber während im debug-Modus, wenn ich die Abfrage derForkJoinPool.commonPool().getParallelism()
bekomme ich das Ergebnis als 7 ( das Prozessor-count - 1 ). Warum ist das so ?main()
. Ich glaube nicht, dass nichts vormain()
schafft eine FJ-pool (aber dies kann variieren, je nach JDK-version). Wenn das fehlschlägt, versuchen Sie die-Eigenschaft auf der Befehlszeile.