Страницы

Поиск по вопросам

вторник, 24 декабря 2019 г.

Изменение атомарного значения из нескольких потоков в Java

#java #многопоточность #атомарность


Здравствуйте.
Помогите, пожалуйста, разобраться со следующей задачей:


  Воспользуйтесь классом LongAccumulator для вычисления максимального и
  минимального накапливаемых элементов.


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

import java.util.Random;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.LongAccumulator;

public class Main {
    public static LongAccumulator maxValue = new LongAccumulator(Math::max, Integer.MIN_VALUE);
    public static LongAccumulator minValue = new LongAccumulator(Math::min, Integer.MAX_VALUE);

    public static void main(String[] args){
        Random random = new Random();
//        Executor executor = Executors.newCachedThreadPool();
        Executor executor = Executors.newFixedThreadPool(1);
        for (int i = 1; i <= 1000; i++) {
            int taskId = i;
            Runnable task = () -> {
                for (int k = 1; k <= 100_000; k++){
                    int value = random.nextInt(1_000_000);
                    maxValue.accumulate(value);
                    minValue.accumulate(value);
                }
                System.out.println(taskId + ": min " + minValue.get() + ": max "
+ maxValue.get());
            };
            executor.execute(task);
        }
    }
}


Всё работает, но не так, как я ожидал. Если все задачи поместить в один поток (см.
строку Executor executor = Executors.newFixedThreadPool(1);), то всё отрабатывает более-менее
быстро.
Если же вместо этой строки подставить Executor executor = Executors.newCachedThreadPool();,
или поставить несколько потоков: Executor executor = Executors.newFixedThreadPool(5);,
то код работает в несколько раз дольше.
Собственно, вопрос состоит в том, почему это происходит и как сделать так, чтобы
разделение задач на потоки не увеличивало время работы программы, а уменьшало.

UPD.
По совету, приведённому в комментариях, изменил код следующим образом, и программа
стала работать быстрей в многопоточном режиме:

import java.util.Random;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.LongAccumulator;

public class Main {
    public static LongAccumulator maxValue = new LongAccumulator(Math::max, Integer.MIN_VALUE);
    public static LongAccumulator minValue = new LongAccumulator(Math::min, Integer.MAX_VALUE);

    public static void main(String[] args){
        Executor executor = Executors.newCachedThreadPool();
        Runnable task; // вынес объявление из цикла ниже
        for (int i = 1; i <= 1000; i++) {
            Random random = new Random();  // для каждой задачи создаётся свой Random
            int taskId = i;
            task = () -> {
                for (int k = 1; k <= 100_000; k++){
                    int value = random.nextInt(1_000_000);
                    maxValue.accumulate(value);
                    minValue.accumulate(value);
                }
                System.out.println(taskId + ": min " + minValue.get() + ": max "
+ maxValue.get());
            };
            executor.execute(task);
        }
    }
}

    


Ответы

Ответ 1



Вынесите объявление Runnable класса из цикла. 1000 потоков (для cachedPool будет именнно так) не даст эффективности - jvm будет между ними переключаться постоянно, а т.к. тело задачи довольно "быстрое" то и эффекта это особо не даст. Поставьте кол-во потоков равным количеству ядер вашего CPU. Random для каждой задачи используйте свой

Комментариев нет:

Отправить комментарий