Sobes.tech
Middle+

Qu'est-ce que le framework Fork/Join et comment est-il utilisé en Java?

sobes.tech IA

Réponse de l'IA

Le cadre Fork/Join est une implémentation spécialisée du cadre d'exécution Executor, conçue pour paralléliser efficacement les tâches qui peuvent être divisées récursivement en sous-tâches plus petites, puis fusionnées (join) leurs résultats. Il repose sur le principe de "diviser pour régner" (divide and conquer).

Composants clés:

  • ForkJoinPool: Un pool de threads qui gère l'exécution des tâches. Il utilise un mécanisme de "work-stealing", où les threads inactifs dans le pool peuvent "voler" des tâches d'autres threads occupés.
  • ForkJoinTask: Classe abstraite représentant une tâche pouvant être exécutée dans ce cadre. Elle a deux sous-types:
    • RecursiveAction: Tâche qui ne retourne pas de résultat.
    • RecursiveTask<V>: Tâche qui retourne un résultat de type V.

Utilisation:

  1. Créer une classe héritant de RecursiveAction ou RecursiveTask.
  2. Surcharger la méthode compute(). Dans cette méthode, décrire la logique:
    • Si la tâche est suffisamment petite (inférieure à un seuil), l'exécuter directement.
    • Sinon, diviser la tâche en sous-tâches, qui s'exécutent de manière asynchrone via fork().
    • Attendre la fin des sous-tâches avec join() et fusionner leurs résultats.
  3. Créer une instance de ForkJoinPool.
  4. Soumettre la tâche au pool en utilisant invoke() ou submit().

Exemple:

import java.util.concurrent.RecursiveTask;
import java.util.concurrent.ForkJoinPool;

class SumArrayTask extends RecursiveTask<Long> {
    private final long[] array;
    private final int start;
    private final int end;
    private static final int THRESHOLD = 1000; // Seuil

    public SumArrayTask(long[] array, int start, int end) {
        this.array = array;
        this.start = start;
        this.end = end;
    }

    @Override
    protected Long compute() {
        if (end - start <= THRESHOLD) {
            long sum = 0;
            for (int i = start; i < end; i++) {
                sum += array[i];
            }
            return sum;
        } else {
            int mid = start + (end - start) / 2;
            SumArrayTask leftTask = new SumArrayTask(array, start, mid);
            SumArrayTask rightTask = new SumArrayTask(array, mid, end);

            leftTask.fork();
            Long rightResult = rightTask.compute();
            Long leftResult = leftTask.join();

            return leftResult + rightResult;
        }
    }
}

// Utilisation dans le code principal
// long[] data = new long[10000]; // Données initiales
// // Remplir data
// ForkJoinPool pool = new ForkJoinPool();
// long sum = pool.invoke(new SumArrayTask(data, 0, data.length));
// pool.shutdown(); // Fermer le pool après utilisation

Avantages:

  • Gestion automatique du pool de threads.
  • Répartition efficace de la charge grâce au "work-stealing".
  • Performance améliorée pour les tâches de type "divide and conquer".

Inconvénients:

  • Non adapté à tous les types de tâches parallèles.
  • Peut être plus difficile à implémenter comparé à d'autres approches pour des tâches simples.