Sobes.tech
Middle+

Ce este framework-ul Fork/Join și cum este utilizat în Java?

sobes.tech AI

Răspuns de la AI

Framework-ul Fork/Join este o implementare specializată a framework-ului Executor, proiectată pentru paralelizarea eficientă a sarcinilor care pot fi împărțite recursiv în sub-sarcini mai mici și apoi unite (join) rezultatele lor. Se bazează pe principiul "divide și cucerește" (divide and conquer).

Componente cheie:

  • ForkJoinPool: Un pool de fire de execuție care gestionează executarea sarcinilor. Utilizează un mecanism de "work-stealing", în care firele inactive din pool pot "fura" sarcini de la alte fire ocupate.
  • ForkJoinTask: Clasă abstractă care reprezintă o sarcină ce poate fi executată în acest framework. Are două subtipuri:
    • RecursiveAction: Sarcină care nu returnează rezultat.
    • RecursiveTask<V>: Sarcină care returnează un rezultat de tip V.

Utilizare:

  1. Se creează o clasă care moștenește de la RecursiveAction sau RecursiveTask.
  2. Se suprascrie metoda compute(). În această metodă se descrie logica:
    • Dacă sarcina este suficient de mică (mai mică decât un prag), se execută direct.
    • În caz contrar, sarcina se împarte în sub-sarcini, care sunt executate asincron prin fork().
    • Se așteaptă finalizarea sub-sarcinilor cu join() și se combină rezultatele.
  3. Se creează o instanță de ForkJoinPool.
  4. Sarcina este trimisă în pool folosind invoke() sau submit().

Exemplu:

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; // Prag

    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;
        }
    }
}

// Utilizare în codul principal
// long[] data = new long[10000]; // Date de intrare
// // Umpleți datele
// ForkJoinPool pool = new ForkJoinPool();
// long sum = pool.invoke(new SumArrayTask(data, 0, data.length));
// pool.shutdown(); // Închideți pool-ul după utilizare

Avantaje:

  • Management automat al pool-ului de fire.
  • Distribuție eficientă a sarcinii datorită "work-stealing".
  • Performanță îmbunătățită pentru sarcini de tip "divide and conquer".

Dezavantaje:

  • Nu este potrivit pentru toate tipurile de sarcini paralele.
  • Poate fi mai dificil de implementat comparativ cu alte abordări pentru sarcini simple.