Преимущество использования forEachOrdered с параллельными потоками

В официальной документации Oracle говорится:

Обратите внимание, что вы можете потерять преимущества parallelism, если используете таких как forEachOrdered с параллельными потоками.

Почему кто-то использует forEachOrdered с параллельным потоком, если мы теряем parallelism?

Ответ 1

в зависимости от ситуации, вы не теряете все преимущества parallelism, используя ForEachOrdered.

Предположим, что у нас есть что-то как таковое:

stringList.parallelStream().map(String::toUpperCase)
                           .forEachOrdered(System.out::println);

В этом случае мы можем гарантировать, что операция терминала ForEachOrdered будет печатать строки в верхнем регистре в порядке встречи, но мы не должны предполагать, что элементы будут переданы промежуточной операции map в том же порядке они были отобраны для обработки. Операция map будет выполняться одновременно несколькими потоками. Таким образом, все еще может выиграть от parallelism, но это просто не использует весь потенциал parallelism. В заключение мы должны использовать ForEachOrdered, когда необходимо выполнить действие в порядке вызова потока.

изменить следующий комментарий:

Что происходит, когда вы пропускаете операцию map? Меня больше интересует ForEachOrdered сразу после parallelStream()

если вы имеете в виду что-то вроде:

 stringList.parallelStream().forEachOrdered(action);

нет никакой пользы в этом, и я сомневаюсь, что подумали дизайнеры, когда они решили создать метод. в этом случае было бы более целесообразно:

stringList.stream().forEach(action);

чтобы распространить на ваш вопрос "Зачем кому-то использовать forEachOrdered с параллельным потоком, если мы теряем parallelism", скажем, вы хотели выполнить действие над каждым элементом относительно порядка столкновений потоков; в таком случае вам понадобится использовать ForEachOrdered, поскольку операция терминала forEach не является детерминированной при параллельном использовании, поэтому существует одна версия для последовательных потоков и одна специально для параллельных потоков.

Ответ 2

На самом деле у меня вопрос не возникает. Зачем? потому что у вас просто нет альтернативы - у вас столько данных, что вам помогут параллельные потоки (это еще нужно доказать); но все равно вам нужно сохранить порядок - таким образом forEachOrdered. Обратите внимание, что документация говорит, что может, а не будет потерять это точно - вам придется измерять и видеть.

Ответ 3

Я нашел stream(). forEachOrdered() на ~ 50% быстрее, чем его параллельная копия. Плюс параллельный использует по крайней мере один поток из общего пула потоков fork-join, который является - одним меньшим потоком для других параллельных потоков, запущенных в JVM.

public static void main(String[] args) { long start = System.currentTimeMillis(); IntStream.range(0,10000000).parallel().forEachOrdered(i -> { //System.out.println(Thread.currentThread().getName()); int p = 1 * 1 ; }); System.out.println((System.currentTimeMillis() - start)); }