Ответ
Параллельные стримы (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)
Критические аспекты:
- Порядок обработки: Не гарантируется. Операции
forEachвыполняются в произвольном порядке. Для сохранения порядка используйтеforEachOrdered(но это снижает производительность). - Общие ресурсы: Избегайте изменяемого общего состояния (например, несинхронизированных коллекций) внутри лямбда-выражений параллельного стрима — это приведет к состоянию гонки (race condition).
- Не для всех задач: Параллелизм выгоден только для больших объемов данных и операций, не блокирующих потоки (CPU-bound задачи). Для IO-bound задач или маленьких коллекций накладные расходы могут перевесить выгоду.
Использование кастомного пула:
ForkJoinPool customPool = new ForkJoinPool(4);
int result = customPool.submit(() ->
numbers.parallelStream()
.mapToInt(i -> i)
.sum()
).join(); Ответ 18+ 🔞
Да ты посмотри, какая, блядь, интересная хуйня! Эти ваши parallelStream() — они же не с потолка берутся, сука. У них под капотом сидит общий ForkJoinPool, который можно достать через ForkJoinPool.commonPool(). Представляешь? Один на всех, как сортир в коммуналке!
Что там у них внутри, блядь:
- Сколько этих потоков-то? По умолчанию — сколько у тебя ядер в процессоре, минус одно. То есть
Runtime.getRuntime().availableProcessors() - 1. Ну, чтобы один поток, мать его, для себя оставить, наверное. Но меньше одного не будет — а то вообще нихуя не заработает. - Как они работают? Алгоритм называется work-stealing. Это когда ленивый поток, который уже всё сделал, может, сука, подкрасться к другому и украсть у него задачку из очереди! Вот такая справедливость, блядь. Чтобы все пахали, а не один в говне.
А сам ForkJoinPool как, блядь, функционирует?
Принцип "разделяй и властвуй", только в коде. Большую задачу нахуй разбивают на мелкие (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(); // А тут всё собирается в кучку
А теперь, сука, самое важное, где все и обоссываются:
- Порядок — похуй! Операция
forEachможет выдать результат в каком угодно порядке, хоть задом наперёд. Если тебе важен порядок — юзайforEachOrdered, но будь готов, что всё поползёт, как улитка в сахаре. - Общие ресурсы — пиздец! Ни в коем случае, блядь, не лезь из лямбды параллельного стрима в какую-нибудь несинхронизированную коллекцию, чтобы что-то там писать. Это гарантированная гонка (race condition), и ты получишь результат, от которого сам от себя охуеешь. Данные перемешаются так, что мама не горюй.
- Не всё то золото, что блестит. Параллелить — это не магическая таблетка. Если данных — кот наплакал, или операции в основном тормозные (IO-bound, а не CPU-bound), то накладные расходы на организацию всей этой движухи съедят всю выгоду. Получится только медленнее и сложнее.
Ну а если тебе commonPool как серпом по яйцам? Можно свой, кастомный пул навернуть:
ForkJoinPool customPool = new ForkJoinPool(4); // Сделал на 4 потока
int result = customPool.submit(() ->
numbers.parallelStream()
.mapToInt(i -> i)
.sum()
).join(); // И ждём, пока всё посчитают
Вот так, блядь. Инструмент мощный, но если использовать его, как мартышка гранату, можно себе всю систему так подвесить, что мало не покажется. Думай головой, а не жопой!