Какой механизм и пул потоков используются для параллельных стримов в Java?

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

Ответ

Параллельные стримы (parallelStream()) используют под капотом общий ForkJoinPool, доступный через ForkJoinPool.commonPool().

Основные характеристики:

  • Размер пула по умолчанию: равен количеству доступных процессоров (ядер) минус один, т.е. Runtime.getRuntime().availableProcessors() - 1. Минимум — 1.
  • Алгоритм: Work-stealing. Потоки, завершившие свои задачи, могут "воровать" задачи из очереди других потоков, что улучшает балансировку нагрузки.

Как работает ForkJoinPool: Принцип "разделяй и властвуй" (divide-and-conquer). Большая задача рекурсивно разбивается на подзадачи (fork), которые выполняются параллельно, а затем их результаты объединяются (join).

Пример и важные замечания:

List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

int sum = numbers.parallelStream() // Используется commonPool
                 .mapToInt(i -> i * 2)
                 .sum(); // Операция сокращения (reduction)

Критические аспекты:

  1. Порядок обработки: Не гарантируется. Операции forEach выполняются в произвольном порядке. Для сохранения порядка используйте forEachOrdered (но это снижает производительность).
  2. Общие ресурсы: Избегайте изменяемого общего состояния (например, несинхронизированных коллекций) внутри лямбда-выражений параллельного стрима — это приведет к состоянию гонки (race condition).
  3. Не для всех задач: Параллелизм выгоден только для больших объемов данных и операций, не блокирующих потоки (CPU-bound задачи). Для IO-bound задач или маленьких коллекций накладные расходы могут перевесить выгоду.

Использование кастомного пула:

ForkJoinPool customPool = new ForkJoinPool(4);
int result = customPool.submit(() -> 
    numbers.parallelStream()
           .mapToInt(i -> i)
           .sum()
).join();