То есть фактор, количество элементов умножить на стоимость обработки каждого элемента, должен быть большим для использования параллельных стрим потоков.
В параллельных стрим потоках лучше всего обрабатывать структуры несвязанных данных, например, ArrayList. LinkedList лучше не использовать, так как в последовательном списке все элементы связаны с предыдущими/последующими элементами. И такие данные трудно распараллелить.
Кроме того, так как все параллельные стрим операции используют один и тот же общий пул потоков ForkJoinPool, нужно избегать блокирования потоков или по крайней мере использовать ManagedBlocker.
Если мы хотим применить долго выполняющиеся операции над элементами коллекции, имеет смысл выделить вычисление стрима в отдельный пул потоков ForkJoinPool.
Здесь мы обертываем вычисление потока в отдельную задачу и передаем ее в отдельный пул потоков.
Если мы не уверены, что на каком-то этапе работы с параллельным стрим, он адекватно сможет выполнить какую-нибудь операцию, мы можем преобразовать этот стрим в последовательный с помощью вызова метода sequential.
Как правило, элементы передаются в поток в том же порядке, в котором они определены в источнике данных.
Сохранение порядка в параллельных потоках увеличивает издержки при выполнении.
Но если нам порядок не важен, то мы можем отключить его сохранение и тем самым увеличить производительность, использовав метод unordered.
В некоторых случаях сохранение порядка в параллельных стрим является важным, например, когда мы хотим отсортировать элементы и вывести их в печать.
В этом примере элементы будут разбиты на потоки, и операция forEach для любого данного элемента может выполняться в любое время и в любом потоке. Поэтому вывод будет неотсортированным.
Если использовать метод forEachOrdered, он заставит обрабатывать элементы по порядку, однако это уберет все преимущество параллельного вычисления.
Метод forEachOrdered обрабатывает элементы стрима в порядке, заданном его источником, независимо от того, выполняете ли вы стрим последовательно или параллельно.
Промежуточные операции подразделяются на stateless и stateful операции.
Stateful операции, distinct, sorted, limit, skip, должны учитывать состояние из ранее обработанных элементов при обработке новых элементов.
Для вычисления результата с учетом состояния требуется обработка всего ввода.
Например, нельзя отсортировать поток до тех пор, пока не будут видны все элементы потока.
В результате при параллельном вычислении, стримы, содержащие промежуточные операции с промежуточным состоянием, требуют нескольких проходов обработки данные или требуют буферизации данных.
В отличие от этого, стримы, содержащие промежуточные операции без состояния, могут обрабатываться за один проход.
Поэтому stateful операции замедляют выполнение параллельных стримов.
Также сами параметры или лямбда выражения стрим операций могут быть stateless и stateful.
Лямбда stateful выражение — это выражение, результат которого зависит от любого состояния, которое может измениться при выполнении стрима.
Здесь мы вычисляем сумму целых чисел, отбрасывая дублирующие элементы, так как метод add класса HashSet добавляет только те элементы, которых еще нет в наборе.
То есть здесь мы используем лямбда выражение, которое должно учитывать состояние ранее обработанных элементов при обработке новых элементов.
И здесь мы не используем операцию distinct, которая автоматически учитывает все элементы стрима.
Соответственно, здесь элементы будут разбиты на группы, каждая из которых будет обрабатываться в своем потоке операцией add.
В каждой группе операция add отбросит дубликаты и все группы просуммируются.
Но так как дубликаты могут быть в соседних группах и при каждом таком выполнении элементы будут разбиваться на группы по-разному, результат вычисления будет разным.
Поэтому при вычислении параллельных стримов нельзя использовать stateful лямбда выражение, так как мы каждый раз будем получать разные результаты для одного и того же ввода.
Теперь, лямбда выражения операций параллельных стримов не должны давать сторонний эффект, то есть они не должны модифицировать другие данные программы или не должны изменяться другими элементами программы.
Здесь мы в параллельных потоках пытаемся модифицировать ArrayList.
При модификации ArrayList один поток увеличивает низлежащий массив и пытается скопировать туда данные.
В это время другой поток успевает туда скопировать свои данные.
Тогда при копировании данных первым потоком возникает исключение ArrayIndexOutOfBoundsException.
Если же мы здесь синхронизируем ArrayList, мы потеряем все преимущество параллелизма.
Также, лямбда-выражения в стрим операциях не должны интерферировать.
Интерференция возникает, когда источник стрима изменяется, при обработке стрима.
Эта ситуация похожа на сторонний эффект, только здесь модифицируется не внешний источник, а источник стрима.
Стримы позволяют выполнять параллельные операции над различными источниками данных, включая даже потоко небезопасные коллекции, такие как ArrayList.
Вильям Л Саймон , Вильям Саймон , Наталья Владимировна Макеева , Нора Робертс , Юрий Викторович Щербатых
Зарубежная компьютерная, околокомпьютерная литература / ОС и Сети, интернет / Короткие любовные романы / Психология / Прочая справочная литература / Образование и наука / Книги по IT / Словари и Энциклопедии