Основная цель, ради которой в Java 8 был добавлен Stream API – удобство многопоточной обработки.
Обычный стрим будет выполняться параллельно после вызова
промежуточной операции parallel(). Некоторые стримы создаются уже многопоточными, например результат вызова
Collection#parallelStream(). Для распараллеливания используется единый общий
ForkJoinPool.
Внутри реализации потока его
сплиттератор оборачивается в
AbstractTask, который и отправляется на выполнение в пул.
AbstractTask при выполнении считывает
estimateSize сплиттератора и текущую степень параллелизма пула. На основе этих данных он принимает решение, распараллелить ли сплиттератор на два методом
trySplit().
У удобства такого решения есть обратная сторона. Так как пул единый, нагрузка распределяется на всех пользователей параллельных стримов в программе. Если в одном потоке выполняются долгие блокирующие операции, это может ударить по производительности в совершенно не связанном с ним другом потоке.
Если всё же требуется использовать отдельный пул потоков, сам стрим выполняется как задача этого отдельного пула.
Подробнее в статье.