Java 8: LongAdder и LongAccumulator предпочтительнее, чем AtomicLong?

2

LongAdder в качестве альтернативы AtomicLong

ExecutorService executor = Executors.newFixedThreadPool(2);    
IntStream.range(0, 1000)
    .forEach(i -> executor.submit(adder::increment));    
stop(executor);    
System.out.println(adder.sumThenReset());   // => 1000

LongAccumulator является более обобщенным вариантом LongAdder

LongBinaryOperator op = (x, y) -> 2 * x + y;
LongAccumulator accumulator = new LongAccumulator(op, 1L);

ExecutorService executor = Executors.newFixedThreadPool(2);
IntStream.range(0, 10)
    .forEach(i -> executor.submit(() -> accumulator.accumulate(i)));
stop(executor);

System.out.println(accumulator.getThenReset());     // => 2539

У меня есть несколько запросов.

  • Является ли LongAdder предпочтительным для AtomicLong?
  • Является ли LongAccumulator предпочтительным как для LongAdder, так и для AtomicLong?
Теги:
java-8
concurrency

2 ответа

3
Лучший ответ

Различие между этими классами, а также использование одного над другим, упоминается в Javadoc. Из LongAdder:

Этот класс обычно предпочтительнее AtomicLong, когда несколько потоков обновляют общую сумму, которая используется для целей сбора статистики, а не для мелкозернистого управления синхронизацией. В условиях низкого уровня обновления два класса имеют схожие характеристики. Но в условиях высокой конкуренции ожидаемая пропускная способность этого класса значительно выше, за счет более высокого потребления пространства.

И из LongAccumulator:

Этот класс обычно предпочтительнее AtomicLong, когда несколько потоков обновляют общее значение, которое используется для целей сбора статистики, а не для мелкозернистого управления синхронизацией. В условиях низкого уровня обновления два класса имеют схожие характеристики. Но в условиях высокой конкуренции ожидаемая пропускная способность этого класса значительно выше, за счет более высокого потребления пространства.

[...]

Класс LongAdder предоставляет аналоги функциональности этого класса для общего частного случая поддержания отсчетов и сумм. Вызов new LongAdder() эквивалентен new LongAccumulator((x, y) -> x + y, 0L).

Таким образом, использование одного над другим зависит от того, что намеревается сделать ваше приложение. Это не всегда строго предпочтительнее, только когда ожидается высокий concurrency, и вам нужно поддерживать общее состояние.

  • 0
    Получил разницу сейчас. Можете ли вы объяснить мне бинарный оператор в приведенном выше фрагменте кода?
  • 2
    @ravindra LongBinaryOperator ? Это функциональный интерфейс , определяющий функциональный метод , принимая 2 long S в качестве параметра и возвращающая long . Таким образом, он может быть использован для работы с двумя лонгами и возврата лота из некоторых вычислений. Например, суммирование 2 LongBinaryOperator longAdder = (x, y) -> x + y; : LongBinaryOperator longAdder = (x, y) -> x + y;
1

@Тунаки ответил на вопрос умело, но есть еще одна проблема, которая влияет на выбор.

Для добавления в ячейку требуется ячейка для каждого потока. Внутренний код использует getProbe() в Striped64, который возвращает:

возвращает UNSAFE.getInt(Thread.currentThread(), PROBE);

Зонд - это системное поле, используемое в threadLocalRandomSeed.

Я понимаю, что зонд уникален для каждого потока. Если у вас есть большое количество потоков create/destroy, тогда создается зонд для каждого нового потока.

Следовательно, количество ячеек может стать чрезмерным. Если у кого-то есть более подробные сведения об этом, я хотел бы услышать от вас.

  • 0
    Кажется, вы разместили свой ответ в неправильном вопросе.
  • 0
    @ravindra как так? Вопрос о выборе, я ответил на вопрос.
Показать ещё 4 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню