Sobes.tech
Middle+

O que é o framework Fork/Join e como é utilizado em Java?

sobes.tech IA

Resposta da IA

A estrutura Fork/Join é uma implementação especializada do framework Executor, projetada para paralelizar tarefas de forma eficiente, que podem ser divididas recursivamente em sub-tarefas menores e depois unir (join) os seus resultados. Baseia-se no princípio de "dividir e conquistar" (divide and conquer).

Componentes principais:

  • ForkJoinPool: Um pool de threads que gere a execução de tarefas. Utiliza um mecanismo de "work-stealing", onde threads ociosos no pool podem "roubar" tarefas de outros threads ocupados.
  • ForkJoinTask: Classe abstrata que representa uma tarefa que pode ser executada neste framework. Tem dois subtipos:
    • RecursiveAction: Tarefa que não devolve resultado.
    • RecursiveTask<V>: Tarefa que devolve um resultado do tipo V.

Utilização:

  1. Criar uma classe que herde de RecursiveAction ou RecursiveTask.
  2. Sobrescrever o método compute(). Neste método, descreve-se a lógica:
    • Se a tarefa for suficientemente pequena (menor que um limiar), ela é executada diretamente.
    • Caso contrário, a tarefa é dividida em sub-tarefas, que são executadas de forma assíncrona usando fork().
    • Espera-se a conclusão das sub-tarefas com join() e une-se os seus resultados.
  3. Criar uma instância de ForkJoinPool.
  4. Submeter a tarefa ao pool usando invoke() ou submit().

Exemplo:

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

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

// Uso no código principal
// long[] data = new long[10000]; // Dados iniciais
// // Preencher data
// ForkJoinPool pool = new ForkJoinPool();
// long sum = pool.invoke(new SumArrayTask(data, 0, data.length));
// pool.shutdown(); // Fechar o pool após uso

Vantagens:

  • Gestão automática do pool de threads.
  • Distribuição eficiente de carga graças ao "work-stealing".
  • Melhor desempenho para tarefas do tipo "divide e conquistar".

Desvantagens:

  • Não adequado para todos os tipos de tarefas paralelas.
  • Pode ser mais difícil de implementar em comparação com outras abordagens para tarefas simples.