Страницы

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

понедельник, 26 ноября 2018 г.

Изменение атомарного значения из нескольких потоков в 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); } } }


Ответ

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

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

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