Что такое ForkJoinPool в Java и для каких задач он предназначен?

«Что такое ForkJoinPool в Java и для каких задач он предназначен?» — вопрос из категории Java Core, который задают на 24% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

ForkJoinPool — это специализированная реализация ExecutorService, оптимизированная для эффективного выполнения рекурсивных задач, которые можно разбивать на более мелкие подзадачи (алгоритмы «разделяй и властвуй»).

Ключевые особенности:

  • Work-Stealing алгоритм: Каждый рабочий поток имеет свою двустороннюю очередь (deque). Когда поток завершает свои задачи, он может «украсть» задачу из хвоста очереди другого занятого потока. Это эффективно балансирует нагрузку.
  • Рекурсивное разбиение: Задачи обычно наследуются от RecursiveTask<V> (возвращает результат) или RecursiveAction (не возвращает).
  • Пул по умолчанию: ForkJoinPool.commonPool() используется многими параллельными операциями в Java (например, parallelStream()).

Пример: вычисление числа Фибоначчи (для демонстрации, неэффективно на практике):

import java.util.concurrent.*;

class FibonacciTask extends RecursiveTask<Integer> {
    final int n;
    FibonacciTask(int n) { this.n = n; }

    @Override
    protected Integer compute() {
        if (n <= 1) return n;
        FibonacciTask f1 = new FibonacciTask(n - 1);
        f1.fork(); // Асинхронный запуск подзадачи в другом потоке
        FibonacciTask f2 = new FibonacciTask(n - 2);
        return f2.compute() + f1.join(); // Дожидаемся результата f1 и объединяем
    }
}

public class Main {
    public static void main(String[] args) {
        ForkJoinPool pool = new ForkJoinPool();
        int result = pool.invoke(new FibonacciTask(10));
        System.out.println(result); // 55
    }
}

Когда использовать:

  • CPU-bound задачи с естественным рекурсивным разбиением (сортировка, обход деревьев, параллельные вычисления).
  • Не подходит для задач, связанных с блокирующим I/O, так как потоки будут простаивать.