Ответ
Параллельный stream в Java позволяет обрабатывать элементы коллекции одновременно в нескольких потоках, используя ForkJoinPool. Основное отличие от sequential stream — автоматическое распараллеливание операций.
Пример:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.parallelStream()
.mapToInt(Integer::intValue)
.sum();
Особенности:
- Порядок обработки не гарантируется
- Требует потокобезопасных операций
- Может быть медленнее sequential для небольших коллекций из-за накладных расходов
- Использует общий ForkJoinPool (можно переопределить через custom pool)
Лучше применять для CPU-intensive операций над большими данными. Для IO-bound задач лучше использовать CompletableFuture.
Ответ 18+ 🔞
А, ну это ж про эти ваши параллельные стримы в Java, да? Сидишь такой, пишешь код, и думаешь: "А не распараллелить ли мне эту хуйню, чтобы быстрее работало?" Ну, в теории-то звучит охуенно.
Смотри, в чём прикол. Берёшь обычный листик с циферками:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
int sum = numbers.parallelStream()
.mapToInt(Integer::intValue)
.sum();
Видишь этот parallelStream()? Это как крикнуть: "Эй, народ, собираемся, будем работать дружно!" Вместо того чтобы один поток, как лох, всё пережёвывал, работа делится на несколько. Используется для этого ForkJoinPool — такая хитрая штука, которая задачи дробит и раздаёт.
Но вот тут, ёпта, начинаются нюансы, про которые все забывают, а потом охуевают.
Во-первых, порядок — пошёл нахуй. Ты передал список [1, 2, 3, 4, 5], а обрабатываться они могут в разброс: сначала 4, потом 1, потом 5. Если твоя операция от порядка зависит — пиши пропало, получишь пиздец, а не результат.
Во-вторых, операции должны быть потокобезопасными, ёбана! Нельзя вот так просто взять и изменять общую переменную из всех потоков. Закончится всё тем, что данные превратятся в кашу, а ты будешь неделю искать, где же эта ебучая гонка.
В-третьих, и это самое важное: не для всего это волшебная таблетка! Если у тебя коллекция из пяти элементов, как в примере, то накладные расходы на создание пула, разделение работы и сбор результатов съедят всю выгоду. Получится даже медленнее, чем сделать всё в одном потоке. Это как вызывать овердохуища грузчиков, чтобы передвинуть один стул.
И наконец, он использует общий пул потоков на всю программу. Если ты в десяти местах запустил parallelStream() для тяжёлых задач, они все будут драться за одни и те же ресурсы, и всё встанет колом. Можно, конечно, свой пул настроить, но это уже высший пилотаж.
Так когда его тогда использовать-то, спросишь? Да когда операций — дохуя, и они действительно сложные, "тяжёлые" для процессора. Какую-нибудь математику там, преобразования изображений. А если твоя задача — просто по сети сходить или в базу данных ткнуть (IO-bound задачи), то забудь про parallelStream как страшный сон. Для такого есть CompletableFuture, вот это уже инструмент, блядь.
Короче, инструмент мощный, но не игрушечный. Включил — следи за порядком, за потокобезопасностью и за размером данных. А то будет как всегда: хотел как лучше, а получил неожиданный пиздец и три дня дебага.