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