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?Различие между этими классами, а также использование одного над другим, упоминается в Javadoc. Из LongAdder
:
Этот класс обычно предпочтительнее
AtomicLong
, когда несколько потоков обновляют общую сумму, которая используется для целей сбора статистики, а не для мелкозернистого управления синхронизацией. В условиях низкого уровня обновления два класса имеют схожие характеристики. Но в условиях высокой конкуренции ожидаемая пропускная способность этого класса значительно выше, за счет более высокого потребления пространства.
И из LongAccumulator
:
Этот класс обычно предпочтительнее
AtomicLong
, когда несколько потоков обновляют общее значение, которое используется для целей сбора статистики, а не для мелкозернистого управления синхронизацией. В условиях низкого уровня обновления два класса имеют схожие характеристики. Но в условиях высокой конкуренции ожидаемая пропускная способность этого класса значительно выше, за счет более высокого потребления пространства.[...]
Класс
LongAdder
предоставляет аналоги функциональности этого класса для общего частного случая поддержания отсчетов и сумм. Вызовnew LongAdder()
эквивалентенnew LongAccumulator((x, y) -> x + y, 0L)
.
Таким образом, использование одного над другим зависит от того, что намеревается сделать ваше приложение. Это не всегда строго предпочтительнее, только когда ожидается высокий concurrency, и вам нужно поддерживать общее состояние.
@Тунаки ответил на вопрос умело, но есть еще одна проблема, которая влияет на выбор.
Для добавления в ячейку требуется ячейка для каждого потока. Внутренний код использует getProbe() в Striped64, который возвращает:
возвращает UNSAFE.getInt(Thread.currentThread(), PROBE);
Зонд - это системное поле, используемое в threadLocalRandomSeed.
Я понимаю, что зонд уникален для каждого потока. Если у вас есть большое количество потоков create/destroy, тогда создается зонд для каждого нового потока.
Следовательно, количество ячеек может стать чрезмерным. Если у кого-то есть более подробные сведения об этом, я хотел бы услышать от вас.
LongBinaryOperator
? Это функциональный интерфейс , определяющий функциональный метод , принимая 2long
S в качестве параметра и возвращающаяlong
. Таким образом, он может быть использован для работы с двумя лонгами и возврата лота из некоторых вычислений. Например, суммирование 2LongBinaryOperator longAdder = (x, y) -> x + y;
:LongBinaryOperator longAdder = (x, y) -> x + y;