Создание параллельных потоков
parallel() и parallelStream()
«Создание параллельных потоков» — бесплатный урок Java Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Java Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Java Academy содержит 4 уроков всего.
Принцип работы параллельных потоков
Параллельный поток разделяет свои данные и обрабатывает части в нескольких потоках выполнения, а затем объединяет результаты. Цель — увеличить пропускную способность при больших объёмах работы.
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
int sum = IntStream.rangeClosed(1, 1000)
.parallel()
.sum();
System.out.println(sum);
}
}parallel() для существующего потока
Вызов parallel() превращает последовательный поток в параллельный. Он может находиться в любой части конвейера.
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> nums = List.of(1, 2, 3, 4, 5);
int total = nums.stream()
.parallel()
.mapToInt(Integer::intValue)
.sum();
System.out.println(total);
}
}parallelStream() для коллекций
Collection.parallelStream() сразу создаёт параллельный поток — это сокращённая форма записи по сравнению с stream().parallel().
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> words = List.of("a", "bb", "ccc");
int chars = words.parallelStream()
.mapToInt(String::length)
.sum();
System.out.println(chars);
}
}Возврат к последовательному выполнению
sequential() возвращает поток к выполнению в одном потоке. Для всего конвейера действует последний вызов parallel() или sequential().
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
int sum = IntStream.rangeClosed(1, 100)
.parallel()
.sequential()
.sum();
System.out.println(sum);
}
}Проверка с помощью isParallel
isParallel() сообщает, будет ли поток выполняться параллельно.
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
System.out.println(IntStream.range(0, 10).isParallel());
System.out.println(IntStream.range(0, 10).parallel().isParallel());
}
}Одинаковый результат, разное выполнение
Для ассоциативных операций, таких как суммирование, параллельные и последовательные потоки возвращают одинаковый результат. Отличается только стратегия выполнения.
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
int seq = IntStream.rangeClosed(1, 50).sum();
int par = IntStream.rangeClosed(1, 50).parallel().sum();
System.out.println(seq == par);
}
}Общий ForkJoinPool
Параллельные потоки используют общий ForkJoinPool.commonPool(). Его размер по умолчанию равен числу доступных процессоров минус один.
public class Main {
public static void main(String[] args) {
System.out.println(Runtime.getRuntime().availableProcessors());
}
}Порядок выполнения потоков не гарантируется
При использовании forEach параллельные потоки могут выводить элементы в любом порядке, поскольку части завершаются в разное время. Если порядок важен, используйте forEachOrdered.
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
IntStream.rangeClosed(1, 5)
.parallel()
.forEachOrdered(System.out::println);
}
}Сокращение объединяет частичные результаты
Внутри параллельное сокращение вычисляет частичные результаты для каждой части и объединяет их с помощью функции-комбинатора. Для корректной работы операция должна быть ассоциативной.
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
int product = IntStream.rangeClosed(1, 6)
.parallel()
.reduce(1, (a, b) -> a * b);
System.out.println(product);
}
}Сбор данных параллельно
Средства сборщиков поддерживают параллельный сбор, объединяя частичные контейнеры. groupingBy корректно работает в параллельном режиме.
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
public class Main {
public static void main(String[] args) {
List<String> words = List.of("apple", "banana", "avocado", "cherry");
Map<Character, List<String>> byFirst = words.parallelStream()
.collect(Collectors.groupingBy(w -> w.charAt(0)));
System.out.println(byFirst);
}
}Не каждый поток работает быстрее
Включить параллелизм просто, но это не всегда приносит пользу. На небольших наборах данных или при дешёвых операциях параллельное выполнение может быть медленнее из-за накладных расходов на разделение и объединение.
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
long count = IntStream.rangeClosed(1, 10)
.parallel()
.filter(n -> n % 2 == 0)
.count();
System.out.println(count);
}
}Быстрая проверка
Какой пул потоков по умолчанию выполняет параллельные потоки?
Итоги
Вы создавали параллельные потоки:
parallel()преобразует любой поток, аparallelStream()является сокращённой формой для коллекций.sequential()возвращает поток к последовательному выполнению; действует последний вызов.- Они выполняются в общем
ForkJoinPool.commonPool(). - Для ассоциативных операций результаты совпадают с последовательными, но порядок и скорость не гарантируются.
Часто задаваемые вопросы
Урок «Создание параллельных потоков» бесплатный?
Да — полный текст урока «Создание параллельных потоков» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Java Academy, подпишись на CoddyKit PRO. Курс Java Academy содержит 4 уроков всего.
Чему я научусь в уроке «Создание параллельных потоков»?
parallel() и parallelStream() Ты практикуешь Java Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Java Academy?
Предыдущий опыт не требуется. Java Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.
Сколько времени занимает урок «Создание параллельных потоков»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Java Academy?
Да. Каждый урок Java Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Создание параллельных потоков
- Когда параллелизм помогает
- Потокобезопасность и побочные эффекты
- Распространённые ошибки