Le framework Fork / Join est une implémentation de l'interface ExecutorService, à travers laquelle nous pouvons implémenter plusieurs processus. Fork / Join peut être utilisé pour diviser récursivement une grande tâche en plusieurs petites tâches, dans le but d'utiliser pleinement toutes les ressources pour améliorer autant que possible les performances de l'application.
Comme toute implémentation de l'interface ExecutorService, Fork / Join utilise également des pools de threads pour gérer les threads de travail distribué. Ce qui est unique dans le cadre Fork / Join, c'est qu'il utilise l'algorithme de vol de travail. Grâce à cet algorithme, les threads de travail peuvent voler d'autres tâches de threads occupés à exécuter lorsque rien ne peut être fait.
Le noyau du cadre Fork / Join est la classe ForkJoinpool, une sous-classe de la classe AbstractExecutorService. Forkjoinpool implémente l'algorithme de vol de travail principal et peut effectuer un traitement ForkJointask.
Utilisation de base
La première étape de l'utilisation du framework Fork / Join consiste à écrire du code qui effectue des tâches fragmentées. Le code à écrire est similaire au pseudo-code suivant:
Si la tâche est suffisamment petite: exécutez la tâche directement ailleurs: coupez la tâche en deux petites tâches pour exécuter deux petites tâches et attendre le résultat
Utilisez la sous-classe FORKJOINTASK pour encapsuler le code comme ci-dessus. Habituellement, certaines classes fournies par JDK sont utilisées, y compris RecursiveTask (cette classe renverra un résultat) et RecursiveAction.
Après avoir préparé la sous-classe FORKJOINTASK, créez un objet représentant toutes les tâches et passez-le à une méthode invoke () d'une instance Forkjoinpool.
Du flou au clair
Pour aider à comprendre le fonctionnement du framework Fork / Join, nous utilisons un cas pour illustrer: par exemple, brouiller une image. Nous représentons l'image à l'aide d'un tableau entier, où chaque valeur numérique représente la couleur d'un pixel. L'image floue est également représentée par un tableau de la même longueur.
L'exécution de flou est obtenue en traitant chaque pixel représentant l'image. Calculez la moyenne de chaque pixel et ses pixels environnants (la moyenne des trois couleurs primaires du rouge, du jaune et du bleu), et le tableau de résultats résultante est l'image floue. Étant donné que la représentation des images est généralement un grand tableau, l'ensemble du processus prend généralement beaucoup de temps. Le cadre Fork / Join peut être utilisé pour tirer parti des avantages du traitement simultané sur les systèmes multiprocesseurs pour accélérer. Voici une implémentation possible:
package com.zhyea.robin; import java.util.concurrent.recursiveAction; La classe publique Forkblur étend RecursiveAction {private int [] msource; Int privé Mstart; private int mLength; Int privé [] MDESTINATION; // gérer la taille de la fenêtre; Cela doit être un nombre impair. Int privé MBlurWidth = 15; public Forkblur (int [] src, int start, int length, int [] dst) {msource = src; mStart = start; mLength = longueur; mDestination = dst; } protégé void calculaDirectly () {int SidePixels = (mBlurWidth - 1) / 2; pour (int index = mStart; index <mStart + mLength; index ++) {// Calculez la valeur moyenne. float rt = 0, gt = 0, bt = 0; pour (int mi = -sidePixels; mi <= SidePixels; 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; } // Réorganiser le pixel cible. int dpixel = (0xff0000000) | (((int) rt) << 16) | (((int) gt) << 8) | (((int) bt) << 0); mDestination [index] = dpixel; }} ....}Implémentez maintenant la méthode abstraite Compute (), dans laquelle les deux opérations floues sont implémentées, et la division d'une tâche en deux petites tâches est implémentée. Ici, nous décidons simplement d'exécuter directement la tâche ou de le diviser en deux petites tâches en fonction de la longueur du tableau:
statique protégé int sthreshold = 100000; Protected void compute () {if (mLength <sthReshold) {calculaDirectly (); retour; } int split = mLength / 2; invokeAllAl (new Forkblur (msource, mStart, Split, mdestination), new Forkblur (msource, mStart + Split, MLength - Split, mDestination)); }Étant donné que la mise en œuvre des méthodes ci-dessus est définie dans une sous-classe de récurrence, des tâches peuvent être créées et exécutées directement dans un fourkjoinpool. Les étapes spécifiques sont les suivantes:
1. Créez un objet représentant la tâche à effectuer:
// SRC représente un tableau de pixels de l'image source // DST représente les pixels de l'image générée ForkBlur FB = new Forkblur (Src, 0, Src.Length, DST);
2. Créez une instance ForkJoinpool qui exécute la tâche:
ForkJoinPool pool = new ForkJoinPool();
3. Exécutez la tâche:
pool.invoke(fb);
Le code source contient également du code pour créer l'image cible. Pour plus de détails, reportez-vous à l'exemple Forkblur.
Implémentation standard
Pour utiliser le framework Fork / Join pour exécuter des tâches simultanées sur des systèmes multi-core en fonction des algorithmes personnalisés, bien sûr, vous devez implémenter des classes personnalisées (comme la classe Forkblur que nous avons implémentée auparavant). De plus, certaines fonctionnalités du cadre Fork / Join ont été largement utilisées dans Javase. Par exemple, la méthode Parallelsort () de la classe java.util.arrays dans Java8 utilise le framework Fork / Join. Pour plus de détails, veuillez consulter la documentation de l'API Java.
Une autre implémentation du cadre Fork / Join se situe dans le package java.util.streams, qui fait également partie de la fonction Lambda de Java8.