0Pricing
Java Academy · Урок

Создание параллельных потоков

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 — локальная установка не требуется.

Все уроки этого курса

  1. Создание параллельных потоков
  2. Когда параллелизм помогает
  3. Потокобезопасность и побочные эффекты
  4. Распространённые ошибки
← Назад к Java Academy