Sobes.tech
Middle+

Kas yra Fork/Join rėmas ir kaip jis naudojamas Java?

sobes.tech AI

Atsakymas iš AI

„Fork/Join“ rėmas — tai specializuota „Executor“ rėmo realizacija, skirta efektyviam užduočių paralelizavimui, kurios gali būti rekursiškai suskirstytos į mažesnes užduotis ir vėliau sujungtos (join) jų rezultatus. Jis grindžiamas principu „skaldyk ir valdyk“ (divide and conquer).

Pagrindiniai komponentai:

  • ForkJoinPool: srautų baseinas, kuris valdo užduočių vykdymą. Jis naudoja „work-stealing“ mechanizmą, kai laukiančios srovės gali „įsilaužti“ į kitų užimtų srovių užduotis.
  • ForkJoinTask: abstrakti klasė, atstovaujanti užduotį, kuri gali būti vykdoma šiame rėme. Ji turi du tipus:
    • RecursiveAction: užduotis, kuri negrąžina rezultato.
    • RecursiveTask<V>: užduotis, kuri grąžina rezultatą tipo V.

Naudojimas:

  1. Sukuriamas klasės, paveldinčios RecursiveAction arba RecursiveTask.
  2. Perrašomas compute() metodas. Šiame metode aprašoma logika:
    • Jei užduotis pakankamai maža (mažesnė už tam tikrą slenkstį), ji vykdoma tiesiogiai.
    • Kitaip, užduotis padalijama į mažesnes užduotis, kurios vykdomos asinchroniškai fork() metodu.
    • Laukiama ir gaunami rezultatų join() metodu ir jie sujungiami.
  3. Sukuriamas ForkJoinPool egzempliorius.
  4. Užduotis pateikiama į baseiną naudojant invoke() arba submit() metodus.

Pavyzdys:

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

    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) {
            // tiesioginis skaičiavimas mažose diapazonuose
            long sum = 0;
            for (int i = start; i < end; i++) {
                sum += array[i];
            }
            return sum;
        } else {
            // užduoties padalijimas
            int mid = start + (end - start) / 2;
            SumArrayTask leftTask = new SumArrayTask(array, start, mid);
            SumArrayTask rightTask = new SumArrayTask(array, mid, end);

            // asinchroninis mažesnių užduočių vykdymas
            leftTask.fork();
            // vykdymas dešinės užduoties dabartiniame arba kitame prieinamame sraute
            Long rightResult = rightTask.compute();
            // laukia ir gauna kairės užduoties rezultatą
            Long leftResult = leftTask.join();

            // rezultatų sujungimas
            return leftResult + rightResult;
        }
    }
}

// Naudojimas pagrindiniame kode
// long[] data = new long[10000]; // pradiniai duomenys
// // užpildyti duomenis
// ForkJoinPool pool = new ForkJoinPool();
// long sum = pool.invoke(new SumArrayTask(data, 0, data.length));
// pool.shutdown(); // uždaryti baseiną po naudojimo

Privalumai:

  • Automatinis srautų valdymas.
  • Efektyvus apkrovos paskirstymas naudojant work-stealing.
  • Pagerintas našumas „skaldyk ir valdyk“ užduotims.

Trūkumai:

  • Netinka visų tipų paraleliniams užduotims.
  • Gali būti sudėtingesnis įgyvendinimas palyginti su kitomis paprastomis metodikomis.