Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Lernen ForkJoinPool | Best Practices für Multithreading
Multithreading in Java
course content

Kursinhalt

Multithreading in Java

Multithreading in Java

1. Grundlagen der Multithread-Verarbeitung
2. Synchronisierte Sammlungen
3. Hochrangige Synchronisationsmechanismen
4. Best Practices für Multithreading

book
ForkJoinPool

Die ForkJoinPool-Klasse in Java für die Arbeit mit dem Fork/Join-Framework ist genau die Umsetzung davon. Sie bietet Mechanismen zur Verwaltung von Aufgaben, die in kleinere Unteraufgaben unterteilt und parallel ausgeführt werden können.

java

Main

copy
1
ForkJoinPool forkJoinPool = new ForkJoinPool();

ForkJoinPool basiert auf dem Konzept von zwei grundlegenden Aktionen: fork und join. Fork ist eine Aktion, bei der wir eine große Aufgabe in mehrere kleinere Teilaufgaben aufteilen, die parallel ausgeführt werden können. Join ist der Prozess, bei dem die Ergebnisse dieser Teilaufgaben zusammengeführt werden, um das Ergebnis der ursprünglichen Aufgabe zu bilden.

Fallstudie

Stellen Sie sich vor, Sie müssen einen riesigen Datensatz analysieren, der in mehrere kleinere Sätze unterteilt werden kann. Wenn Sie jeden Datensatz separat verarbeiten und dann die Ergebnisse zusammenführen, können Sie die Verarbeitung erheblich beschleunigen, insbesondere auf Mehrprozessorsystemen.

Wie man ForkJoinPool verwendet

Es gibt 2 Klassen, um Aufgaben in ihnen zu implementieren, sie sind RecursiveTask und RecursiveAction. Beide erfordern eine abstrakte compute()-Methode, die implementiert werden muss. In RecursiveTask gibt die compute()-Methode einen Wert zurück, während in RecursiveAction die compute()-Methode void zurückgibt.

java

Main

copy
12345678
class ExampleTask extends RecursiveTask<String> { @Override protected String compute() { // code return null; } }

Wenn wir von einem RecursiveTask erben, müssen wir sicherstellen, welchen Datentyp unsere compute()-Methode zurückgeben wird, indem wir diese Syntax verwenden RecursiveTask<String>.

java

Main

copy
1234567
class ExampleAction extends RecursiveAction { @Override protected void compute() { // code } }

Hier ist es das Gegenteil, wir müssen den Typ in RecursiveAction nicht explizit angeben, da unsere Methode nichts zurückgeben wird und nur die Aufgabe ausführt.

Starten einer Aufgabe

Übrigens können wir eine Aufgabe zur Ausführung starten, ohne ForkJoinPool zu verwenden, indem wir einfach die Methoden fork() und join() verwenden.

Es ist wichtig zu beachten, dass die Methode fork() die Aufgabe an einen Thread sendet. Die Methode join() wird verwendet, um das Ergebnis zu erhalten.

java

Main

copy
12345
public static void main(String[] args) { ExampleTask simpleClass = new ExampleTask(); simpleClass.fork(); System.out.println(simpleClass.join()); }

Hauptmethoden von ForkJoinPool

  • invoke(): Startet eine Aufgabe im Pool und wartet auf deren Abschluss. Gibt das Ergebnis des Abschlusses der Aufgabe zurück;
  • submit(): Reicht eine Aufgabe an den Pool ein, blockiert jedoch nicht den aktuellen Thread, während auf den Abschluss der Aufgabe gewartet wird. Kann verwendet werden, um Aufgaben einzureichen und deren Future-Objekte abzurufen;
  • execute(): Führt eine Aufgabe im Pool aus, gibt jedoch kein Ergebnis zurück und blockiert nicht den aktuellen Thread.

Wie können wir eine Aufgabe mit ForkJoinPool starten, ganz einfach!

java

Main

copy
12345
public static void main(String[] args) { ExampleAction simpleClass = new ExampleAction(); ForkJoinPool forkJoinPool = new ForkJoinPool(); System.out.println(forkJoinPool.invoke(simpleClass)); }

Aber was ist der Unterschied zwischen dem Ausführen von fork(), join() und über die ForkJoinPool Klasse?

Es ist sehr einfach! Wenn wir die erste Methode mit Hilfe der fork(), join() Methoden verwenden, starten wir die Aufgabe im selben Thread, in dem diese Methoden aufgerufen wurden, blockieren diesen Thread, während wir mit Hilfe der ForkJoinPool Klasse einen Thread aus dem Pool zugewiesen bekommen und dieser in diesem Thread arbeitet, ohne den Hauptthread zu blockieren.

Schauen wir uns das genauer an, nehmen wir an, wir haben eine solche Implementierung:

java

Main

copy
12345678
class ExampleRecursiveTask extends RecursiveTask<String> { @Override protected String compute() { System.out.println("Thread: " + Thread.currentThread().getName()); return "Wow, it works!!!"; } }

Und wir möchten diesen Code unter Verwendung von fork() und join() ausführen. Lassen Sie uns sehen, was auf die Konsole gedruckt wird und welcher Thread diese Aufgabe ausführen wird.

java

Main

copy
1234567
public class Main { public static void main(String[] args) { ExampleRecursiveTask simpleClass = new ExampleRecursiveTask(); simpleClass.fork(); System.out.println(simpleClass.join()); } }

Und wir erhalten diese Ausgabe auf der Konsole:

Lassen Sie uns nun überprüfen, was passiert, wenn wir mit ForkJoinPool ausführen:

java

Main

copy
1234567
public class Main { public static void main(String[] args) { ExampleRecursiveTask simpleClass = new ExampleRecursiveTask(); ForkJoinPool forkJoinPool = new ForkJoinPool(); System.out.println(forkJoinPool.invoke(simpleClass)); } }

Und wir kommen zu diesem Schluss:

Und wie Sie sehen können, ist der Unterschied, dass bei Verwendung der 1. Methode die Aufgabe im selben Thread ausgeführt wird, der diese Aufgabe aufgerufen hat (Hauptthread). Aber wenn wir die 2. Methode verwenden, wird der Thread aus dem ForkJoinPool-Thread-Pool entnommen und der Hauptthread, der diese Logik aufgerufen hat, wird nicht blockiert und läuft weiter!

Wie man Fork/Join im Code implementiert

Der einfachste Weg, dies zu erklären, wäre in einem Video, anstatt Ihnen 50-80 Zeilen Code zu geben und es Punkt für Punkt zu erklären.

Zusammenfassung

ForkJoinPool ist effektiv für Aufgaben, die leicht in kleinere Unteraufgaben unterteilt werden können. Wenn die Aufgaben jedoch zu klein sind, kann die Verwendung von ForkJoinPool keinen signifikanten Leistungsgewinn bringen.

War alles klar?

Wie können wir es verbessern?

Danke für Ihr Feedback!

Abschnitt 4. Kapitel 2
We're sorry to hear that something went wrong. What happened?
some-alt