Sobes.tech
Middle+

Co je to framework Fork/Join a jak se používá v Java?

sobes.tech AI

Odpověď od AI

Framework Fork/Join je specializovaná implementace rámce Executor, navržená pro efektivní paralelní rozdělení úkolů, které lze rekurzivně rozdělit na menší podúkoly a poté jejich výsledky sloučit (join). Je založen na principu "dělej a panuj".

Klíčové komponenty:

  • ForkJoinPool: Pool vláken, který řídí vykonávání úkolů. Používá mechanismus "work-stealing", kde neaktivní vlákna mohou "ukrást" úkoly od jiných zaneprázdněných vláken.
  • ForkJoinTask: Abstraktní třída představující úkol, který může být v tomto rámci vykonán. Má dvě podtřídy:
    • RecursiveAction: Úkol, který nevrací výsledek.
    • RecursiveTask<V>: Úkol, který vrací výsledek typu V.

Použití:

  1. Vytvoří se třída, která dědí z RecursiveAction nebo RecursiveTask.
  2. Přepíše se metoda compute(). V této metodě je popsána logika:
    • Pokud je úkol dostatečně malý (menší než určitá práh), je proveden přímo.
    • Jinak je úkol rozdělen na podúkoly, které jsou spuštěny asynchronně metodou fork().
    • Čeká se na dokončení podúkolů metodou join() a jejich výsledky se sloučí.
  3. Vytvoří se instance ForkJoinPool.
  4. Úkol je předán do poolu pomocí metod invoke() nebo submit().

Příklad:

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; // Práh

    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) {
            // Přímý výpočet pro malé rozsahy
            long sum = 0;
            for (int i = start; i < end; i++) {
                sum += array[i];
            }
            return sum;
        } else {
            // Rozdělení úkolu
            int mid = start + (end - start) / 2;
            SumArrayTask leftTask = new SumArrayTask(array, start, mid);
            SumArrayTask rightTask = new SumArrayTask(array, mid, end);

            // Asynchronní spuštění podúkolů
            leftTask.fork();
            Long rightResult = rightTask.compute();
            Long leftResult = leftTask.join();

            // Sloučení výsledků
            return leftResult + rightResult;
        }
    }
}

// Použití v hlavním kódu
// long[] data = new long[10000]; // Výchozí data
// // Naplnit data
// ForkJoinPool pool = new ForkJoinPool();
// long sum = pool.invoke(new SumArrayTask(data, 0, data.length));
// pool.shutdown(); // Uzavření poolu po použití

Výhody:

  • Automatické řízení poolu vláken.
  • Efektivní rozdělení zátěže díky work-stealing.
  • Zlepšený výkon pro úkoly typu "dělej a panuj".

Nevýhody:

  • Není vhodný pro všechny typy paralelních úkolů.
  • Může být složitější na implementaci ve srovnání s jinými přístupy pro jednoduché úkoly.