Почему java.util.Collection#parallelStream утроит время выполнения при использовании одного потока?

Почему время выполнения java.util.Collection#parallelStream не соответствует ожиданию?

Результат cost около 30, ожидайте 10.

    private void process(int i) {
        try {
            Thread.sleep(10000); // 10s
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Test
    public void testPartition() {
        List<Integer> numbers = IntStream.range(1, 21).boxed().collect(Collectors.toList());
        long cost = 0L;

        long jobStart = System.currentTimeMillis();
        numbers.parallelStream().forEach(x -> {
            process(x);
        });

        cost += (System.currentTimeMillis() - jobStart);

        System.out.println("availableProcessors = " + Runtime.getRuntime().availableProcessors());
        System.out.println("cost = " + cost);
    }

Кажется пробегает process(x); 3 раза. почему это?

@daniu прав.

ForkJoinPool в Java использует номер потока по умолчанию, равный количеству доступных процессоров, как сообщает метод Runtime.getRuntime().availableProcessors(). Эта конструкция предназначена для эффективного использования доступных ресурсов ЦП для задач параллельной обработки.

выход:

availableProcessors = 8
cost = 30015

Вероятно, это не имеет значения, но что заставляет вас говорить «использование одного потока»? По умолчанию parallelStream использует стандартный ForkJoinPool IIRC. Почему вы объявляете long[], а затем используете только первый элемент для суммирования времени?

daniu 30.05.2024 10:33

Ожидаете ли вы, что он запустит 20 разных потоков? Почему вы так думаете?

Sweeper 30.05.2024 10:37

еще лучше включить минимально воспроизводимый пример в такие вопросы... например. мы не знаем, что такое Lists (конечно, мы можем это предположить)

user85421 30.05.2024 10:49
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
2
3
66
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Спасибо @daniu. parallelStream использует значение по умолчанию ForkJoinPool. ForkJoinPool в Java использует номер потока по умолчанию, равный количеству доступных процессоров.

availableProcessors = 8

тогда 20/8=3, поэтому стоимость около 30, а не 10.

Кстати, вы можете изменить количество потоков, позвонив ForkJoinPool.commonPool().setParallelism(20) — это должно привести к значению около 10 с для опубликованного кода.

user85421 30.05.2024 11:09

@user85421 user85421 – Если вы используете VirtualThreads, это весьма вероятно. Но что касается аппаратных потоков, я бы сказал, что это зависит от ОС и оборудования. Из Javadoc для Thread::sleep: «Нетка не теряет право собственности ни на один монитор». В некоторых средах это также означает, что ядро ​​не будет освобождено. На моем компьютере с Unix оно было близко к 10, на моем NXT — выше 400 (хорошо, реализация Java на последнем в лучшем случае плохая).

tquadrat 30.05.2024 12:41

Другие вопросы по теме