Ответ
Параллельные потоки в Java Stream API создаются двумя основными способами, которые используют под капотом общий ForkJoinPool.
Способы создания:
-
Метод
Collection.parallelStream()— самый частый способ создать параллельный поток напрямую из коллекции.List<String> list = Arrays.asList("A", "B", "C"); list.parallelStream() .forEach(System.out::println); // Элементы обрабатываются параллельно -
Метод
Stream.parallel()— преобразует существующий последовательный поток в параллельный.Stream.of("A", "B", "C") .parallel() // Преобразование в параллельный поток .forEach(System.out::println);
Важные детали реализации:
- По умолчанию используется
ForkJoinPool.commonPool()— общий пул потоков для всего приложения. - Количество потоков в общем пуле равно
количество_ядер_процессора - 1. - Можно использовать свой
ForkJoinPoolдля изоляции задачи:ForkJoinPool customPool = new ForkJoinPool(4); customPool.submit(() -> list.parallelStream().forEach(...) // Работает в customPool ).get();
Когда использовать: Параллельные потоки эффективны для CPU-интенсивных задач с большими объемами данных, где затраты на распараллеливание окупаются. Для простых операций или IO-задач они могут дать обратный эффект из-за накладных расходов.
Ответ 18+ 🔞
Давай разберём этот ваш параллельный поток, а то как будто про космос речь. Сидишь такой, пишешь на джаве, всё по классике: stream(), filter(), map — красота, душа поёт. А потом бац — нужно быстрее, и тут тебе подсовывают эту штуку — parallelStream(). И вроде как магия, а на деле — обычный ForkJoinPool, только в профиль.
Как его, блядь, создать, этот параллельный поток?
Первым делом, самый частый способ — это взять свою коллекцию и просто, сука, позвать parallelStream(). Всё, приехали. Больше ничего не надо.
List<String> list = Arrays.asList("A", "B", "C");
list.parallelStream()
.forEach(System.out::println); // А тут элементы уже пляшут как хотят, параллельно, понимаешь?
Второй способ — для тех, кто любит извращения. Есть у тебя обычный, последовательный поток. Ну так возьми и скажи ему: «Стань параллельным, ёпта!» Через метод parallel().
Stream.of("A", "B", "C")
.parallel() // Вжух, и он уже не тот, стал параллельным ублюдком
.forEach(System.out::println);
А что там под капотом-то, а?
А под капотом, друг мой, сидит этот самый ForkJoinPool.commonPool() — общий пул на всё приложение, как коммунальная квартира. Потоков в нём обычно: количество твоих ядер процессора минус один. То есть если у тебя 8 ядер, то 7 потоков будут пахать, а одно ядро — за главного считать. Хитро, да?
Но если тебе этот общий пул как серая мышь — не нравится, хочешь изоляции, чтобы твои задачи отдельно шаманили — пожалуйста, создавай свой собственный ForkJoinPool. Это как своя хата, свой огород.
ForkJoinPool customPool = new ForkJoinPool(4); // Четыре работяги, ни больше, ни меньше
customPool.submit(() ->
list.parallelStream().forEach(...) // И вот тут всё крутится уже в твоём личном пуле, а не в общем
).get();
Когда это, блядь, применять-то?
А вот тут, мой друг, собака зарыта. Параллельные потоки — это не волшебная таблетка. Они хороши для тяжёлых вычислений, где данных — овердохуища, и каждому элементу нужно по полчаса мозгами шевелить. Тогда распараллеливание окупится. А если у тебя операция — чих-пых, или ещё хуже — ввод-вывод (IO), где поток просто спит и ждёт, то накладные расходы на организацию всей этой цирковой труппы съедят всю выгоду. Получится только медленнее, вот тебе и вся магия. Так что думай головой, прежде чем везде пихать parallel().