Das Fork/Join -Framework ist eine Implementierung der ExecutorService -Schnittstelle, über die wir mehrere Prozesse implementieren können. Fork/Join kann verwendet werden, um eine große Aufgabe rekursiv in mehrere kleine Aufgaben aufzuteilen, um die vollständige Nutzung aller Ressourcen zu nutzen, um die Leistung der Anwendung so weit wie möglich zu verbessern.
Wie bei jeder Implementierung von ExecutorService -Schnittstellen verwaltet Fork/Join auch Thread -Pools, um Arbeiter -Threads verteilt zu verwalten. Das Einzigartige an dem Fork/Join-Framework ist, dass der Work-Stealing-Algorithmus verwendet wird. Durch diesen Algorithmus können Worker -Threads andere geschäftige Thread -Aufgaben ausführen, wenn nichts erledigt werden kann.
Der Kern des Fork/Join -Frameworks ist die Forkjoinpool -Klasse, eine Unterklasse der AbstractExecutorService -Klasse. Forkjoinpool implementiert den Kernalgorithmus für die Arbeitsstörung und kann die Verarbeitung von Forkjointasking-Verarbeitung durchführen.
Grundnutzung
Der erste Schritt bei der Verwendung des Fork/Join -Frameworks besteht darin, Code zu schreiben, der fragmentierte Aufgaben ausführt. Der zu schriftliche Code ähnelt dem folgenden Pseudo-Code:
Wenn die Aufgabe klein genug ist: Führen Sie die Aufgabe direkt sonst aus: Schneiden Sie die Aufgabe in zwei kleine Aufgaben aus, um zwei kleine Aufgaben auszuführen und auf das Ergebnis zu warten
Verwenden Sie die Forkjointask -Unterklasse, um den Code wie oben zu verkapulieren. Normalerweise werden einige von JDK bereitgestellte Klassen verwendet, einschließlich Rekursiv- (diese Klasse gibt ein Ergebnis zurück) und Recursiveaction.
Erstellen Sie nach der Vorbereitung der Forkjointask -Unterklasse ein Objekt, das alle Aufgaben darstellt, und übergeben Sie sie an eine Invoke () -Methode einer Forkjoinpool -Instanz.
Von Unschärfe zu klären
Um zu verstehen, wie das Fork/Join -Framework funktioniert, verwenden wir einen Fall, um zu veranschaulichen: zum Beispiel ein Bild verwischen. Wir stellen das Bild mit einem Ganzzahlarray dar, wobei jeder numerische Wert die Farbe eines Pixels darstellt. Das verschwommene Bild wird auch durch ein Array derselben Länge dargestellt.
Die Ausführung von Unschärfe wird erreicht, indem jedes Pixel verarbeitet wird, das das Bild darstellt. Berechnen Sie den Mittelwert jedes Pixels und seiner umgebenden Pixel (der Mittelwert der drei Primärfarben von Rot, Gelb und Blau), und die resultierende Auswahl an Ergebnissen ist das verschwommene Bild. Da die Darstellung von Bildern normalerweise ein großes Array ist, dauert der gesamte Vorgang normalerweise viel Zeit. Das Fork/Join -Framework kann verwendet werden, um die Vorteile der gleichzeitigen Verarbeitung von Multiprozessorsystemen zur Beschleunigung der gleichzeitigen Verarbeitung zu nutzen. Hier ist eine mögliche Implementierung:
Paket com.zhyea.robin; Import Java.util.Concurrent.recursiveaction; Public Class Forkblur erweitert Recursiveaction {private int [] msource; privat int mstart; private int mlength; private int [] mdestination; // Fenstergröße der Fenster; Es muss eine ungerade Zahl sein. private int mblurwidth = 15; public forkblur (int [] src, int start, int länge, int [] dst) {msource = src; mstart = start; MLength = Länge; mdestination = dst; } protected void computertirectly () {int Sidepixels = (Mblurwidth - 1) / 2; für (int index = mstart; index <mstart+mLength; Index ++) {// Berechnen Sie den Durchschnittswert. float rt = 0, gt = 0, bt = 0; für (int mi = -Sidepixel; mi <= sidepixel; mi ++) {int mindEx = math.min (math.max (mi+index, 0), msource.length - 1); int pixel = msource [mindEx]; rt += (float) ((pixel & 0x00ff0000) >> 16) / mblurwidth; gt += (float) ((pixel & 0x00000ff00) >> 8) / mblurwidth; bt += (float) ((pixel & 0x000000ff) >> 0) / mblurwidth; } // Das Zielpixel neu organisiert. int dpixel = (0xff0000000) | (((int) rt) << 16) | (((int) gt) << 8) | (((int) bt) << 0); mdestination [index] = dpixel; }} ....}Implementieren Sie nun die abstrakte Methode compute (), in der beide Fuzzy -Operationen implementiert werden, und die Aufteilung einer Aufgabe in zwei kleine Aufgaben wird implementiert. Hier entscheiden wir einfach, ob wir die Aufgabe direkt ausführen oder in zwei kleinen Aufgaben aufteilen, basierend auf der Länge des Arrays:
geschütztes statisches int sthreshold = 100000; protected void compute () {if (mLength <sthreshold) {computeredirecty (); zurückkehren; } int split = mLength / 2; invokeAll (neuer Forkblur (MSource, Mstart, Split, Mdestination), New Forkblur (MSource, Mstart + Split, MLength - Split, Mdestination)); }Da die Implementierung der oben genannten Methoden in einer Rekursionsunterklasse definiert ist, können Aufgaben erstellt und direkt in einem Forkjoinpool ausgeführt werden. Die spezifischen Schritte sind wie folgt:
1. Erstellen Sie ein Objekt, das die zu ausgeführte Aufgabe darstellt:
// src repräsentiert ein Array von Pixeln des Quellbildes // DST repräsentiert die Pixel des generierten Bildgabels fb = new Forkblur (Src, 0, src.length, DST);
2. Erstellen Sie eine Forkjoinpool -Instanz, in der die Aufgabe ausgeführt wird:
ForkJoinPool pool = new ForkJoinPool();
3. Führen Sie die Aufgabe aus:
pool.invoke(fb);
Der Quellcode enthält auch einen Code, um das Zielbild zu erstellen. Einzelheiten finden Sie in Forkblur -Beispiel.
Standardimplementierung
Um das Fork/Join-Framework zu verwenden, um gleichzeitige Aufgaben auf Multi-Core-Systemen nach benutzerdefinierten Algorithmen auszuführen, müssen Sie natürlich benutzerdefinierte Klassen implementieren (wie die zuvor implementierte Forkblur-Klasse). Darüber hinaus wurden einige Funktionen des Fork/Join -Frameworks in Javase häufig verwendet. Beispielsweise verwendet die Parallelsort () -Methode der Klasse Java.util.Arrays in Java8 das Fork/Join -Framework. Weitere Informationen finden Sie in der Dokumentation von Java API.
Eine weitere Implementierung des Fork/Join -Frameworks erfolgt unter dem Paket java.util.streams, das auch Teil der Lambda -Funktion von Java8 ist.