Benutzerdefinierte Thread-Pools in parallelen Java 8-Streams

Benutzerdefinierte Thread-Pools in parallelen Java 8-Streams

1. Überblick

Java 8 führte das Konzept von Streams als effiziente Methode zur Durchführung von Massenoperationen mit Daten ein. In Umgebungen, die Parallelität unterstützen, können paralleleStreams erhalten werden.

Diese Streams können eine verbesserte Leistung aufweisen - auf Kosten des Multithreading-Overheads.

In diesem kurzen Tutorial sehen wir unsone of the biggest limitations of Stream API an und sehen, wie ein paralleler Stream mit einer benutzerdefiniertenThreadPool-Instanz funktioniert, alternativ -there’s a library that handles this.

2. ParallelStream

Beginnen wir mit einem einfachen Beispiel - dem Aufrufen derparallelStream-Methode für einen derCollection-Typen -, das möglicherweise paralleleStream zurückgibt:

@Test
public void givenList_whenCallingParallelStream_shouldBeParallelStream(){
    List aList = new ArrayList<>();
    Stream parallelStream = aList.parallelStream();

    assertTrue(parallelStream.isParallel());
}

Die Standardverarbeitung, die in einem solchenStream auftritt, verwendetForkJoinPool.commonPool(),a Thread Pool shared by the entire application.

3. BenutzerdefinierteThread Pool

Wir können tatsächlich ein benutzerdefiniertesThreadPool übergeben, wenn wir dasstream verarbeiten.

Im folgenden Beispiel können paralleleStream benutzerdefinierteThread Pool verwenden, um die Summe der langen Werte von 1 bis einschließlich 1.000.000 zu berechnen:

@Test
public void giveRangeOfLongs_whenSummedInParallel_shouldBeEqualToExpectedTotal()
  throws InterruptedException, ExecutionException {

    long firstNum = 1;
    long lastNum = 1_000_000;

    List aList = LongStream.rangeClosed(firstNum, lastNum).boxed()
      .collect(Collectors.toList());

    ForkJoinPool customThreadPool = new ForkJoinPool(4);
    long actualTotal = customThreadPool.submit(
      () -> aList.parallelStream().reduce(0L, Long::sum)).get();

    assertEquals((lastNum + firstNum) * lastNum / 2, actualTotal);
}

Wir haben den KonstruktorForkJoinPool mit einer Parallelitätsstufe von 4 verwendet. Es sind einige Experimente erforderlich, um den optimalen Wert für verschiedene Umgebungen zu ermitteln. Eine gute Faustregel ist jedoch, die Anzahl einfach basierend auf der Anzahl der Kerne in Ihrer CPU zu wählen.

Als nächstes haben wir den Inhalt der parallelenStream verarbeitet und sie im Aufruf vonreduce zusammengefasst.

Dieses einfache Beispiel zeigt möglicherweise nicht die volle Nützlichkeit der Verwendung eines benutzerdefiniertenThread Pool, aber die Vorteile werden in Situationen offensichtlich, in denen wir das gemeinsameThread Pool nicht mit lang laufenden Aufgaben verknüpfen möchten (z. Daten von einer Netzwerkquelle verarbeiten) oder die gemeinsamenThread Pool werden von anderen Komponenten in der Anwendung verwendet.

4. Fazit

Wir haben uns kurz angesehen, wie paralleleStream mit benutzerdefiniertenThread Pool ausgeführt werden. In der richtigen Umgebung und mit der richtigen Verwendung der Parallelitätsebene können in bestimmten Situationen Leistungssteigerungen erzielt werden.

Die vollständigen Codebeispiele, auf die in diesem Artikel verwiesen wird, finden Sie inover on Github.