Este tópico aborda como utilizar o Fork/Join Framework, introduzido no Java 7, mas com grande importância no Java 8, especialmente para processamento paralelo e divisão de tarefas recursivas.
🧠 RESUMO
O Fork/Join Framework é usado para dividir tarefas grandes em subtarefas menores, que são executadas paralelamente. Ideal para algoritmos recursivos e que podem ser paralelizados.
🔧 Baseado na ideia de “divide and conquer”.
- 🧩 Classe base:
java.util.concurrent.ForkJoinPool - 🧩 Tarefa base:
RecursiveTask<V>(retorna valor) ouRecursiveAction(não retorna)
🔹 CLASSES PRINCIPAIS
| Classe | Descrição |
|---|---|
ForkJoinPool | Executor especializado em tarefas que usam fork/join. |
RecursiveTask<V> | Usa quando a tarefa retorna um valor. |
RecursiveAction | Usa quando a tarefa não retorna valor. |
🔍 EXEMPLO 1 – Usando RecursiveTask para somar um array
import java.util.concurrent.*;
public class SumTask extends RecursiveTask<Long> {
private final long[] array;
private final int start, end;
private static final int THRESHOLD = 3;
public SumTask(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 middle = (start + end) / 2;
SumTask left = new SumTask(array, start, middle);
SumTask right = new SumTask(array, middle, end);
left.fork(); // Executa em paralelo
long rightResult = right.compute();
long leftResult = left.join(); // Espera a conclusão da esquerda
return leftResult + rightResult;
}
}
public static void main(String[] args) {
ForkJoinPool pool = new ForkJoinPool();
long[] numbers = {1, 2, 3, 4, 5, 6};
SumTask task = new SumTask(numbers, 0, numbers.length);
long result = pool.invoke(task);
System.out.println("Soma: " + result); // 21
}
}
🎯 EXEMPLO 2 – RecursiveAction
import java.util.concurrent.RecursiveAction;
import java.util.concurrent.ForkJoinPool;
public class PrintTask extends RecursiveAction {
private final int start, end;
private static final int THRESHOLD = 3;
public PrintTask(int start, int end) {
this.start = start;
this.end = end;
}
@Override
protected void compute() {
if (end - start <= THRESHOLD) {
for (int i = start; i < end; i++) {
System.out.println("Imprimindo: " + i);
}
} else {
int mid = (start + end) / 2;
invokeAll(new PrintTask(start, mid), new PrintTask(mid, end));
}
}
public static void main(String[] args) {
ForkJoinPool pool = new ForkJoinPool();
pool.invoke(new PrintTask(0, 10));
}
}
⚠️ ERROS COMUNS NA PROVA
- ❌ Usar
fork()semjoin()– sempre use ambos para obter o resultado da subtarefa. - ❌ Esquecer de verificar a condição base (threshold) – isso pode levar à recursão infinita.
- ❌ Confundir
invokeAll()comfork()/join()–invokeAll()é uma maneira conveniente de chamar múltiplas subtarefas. - ❌ Achar que
ForkJoinPoolcria threads infinitamente – ele usa um número limitado de threads (pool de workers).
🧠 DICAS PARA A PROVA
- Use
RecursiveTaskse precisar de retorno (como soma, contagem, busca). - Use
RecursiveActionse a tarefa não precisa retornar (como imprimir, escrever arquivo). fork()inicia a execução assíncrona.join()espera a execução terminar e obtém o resultado.- O método
invoke()doForkJoinPoolinicia a tarefa e bloqueia até terminar.
✅ 10 QUESTÕES DE PRÁTICA
1. O que faz a classe RecursiveTask<V>?
A) Executa threads em paralelo
B) Realiza tarefas que não retornam valor
C) Realiza tarefas com retorno de valor ✅
D) Cria um novo ForkJoinPool
2. Qual é o método usado para executar uma tarefa no ForkJoinPool?
A) run()
B) start()
C) invoke() ✅
D) submit()
3. O que fork() faz?
A) Espera outra thread terminar
B) Executa uma subtarefa de forma assíncrona ✅
C) Cria um novo thread pool
D) Invoca todas as tarefas ao mesmo tempo
4. Qual método é usado para esperar o resultado de uma subtarefa?
A) get()
B) join() ✅
C) await()
D) sleep()
5. Quando devemos usar RecursiveAction?
A) Sempre que o método retorna um valor
B) Quando não há retorno da tarefa ✅
C) Quando usamos threads comuns
D) Nunca, pois está obsoleta
6. ForkJoinPool usa quantas threads por padrão?
A) 1
B) Ilimitadas
C) Número de núcleos disponíveis ✅
D) Sempre 2
7. Qual é a vantagem do Fork/Join Framework?
A) Serialização eficiente
B) Divisão automática e paralelismo ✅
C) Uso de memoria mínimo
D) Evita recursão
8. O que acontece se join() for chamado antes de fork()?
A) Executa corretamente
B) Pode causar deadlock ✅
C) Otimiza a execução
D) Nada muda
9. Qual é o papel do threshold no Fork/Join?
A) Define quantas threads o pool pode usar
B) Define o número de tarefas simultâneas
C) Controla quando parar de dividir e resolver diretamente ✅
D) Controla o tempo máximo de execução
10. invokeAll(t1, t2) faz o quê?
A) Executa t1 e ignora t2
B) Executa t1 e t2 sequencialmente
C) Executa ambas em paralelo e espera as duas terminarem ✅
D) Executa uma thread infinita