Ответ
Использование synchronized для счетчика при большом количестве конкурирующих потоков приводит к высокому contention (состязанию). Потоки выстраиваются в очередь для захвата одного монитора, что вызывает:
- Накладные расходы на переключение контекста.
- Простои потоков в состоянии BLOCKED.
- Резкое падение пропускной способности (throughput).
Альтернативы из пакета java.util.concurrent.atomic:
-
AtomicLong/AtomicIntegerИспользуют CAS (Compare-And-Swap) операции на уровне процессора, что позволяет обновлять значение без полной блокировки.private AtomicLong counter = new AtomicLong(); public long increment() { return counter.incrementAndGet(); // Неблокирующая операция } -
LongAdder/DoubleAdder(Java 8+) Оптимизированы для сценариев с высокой записной нагрузкой. Значение разделяется на несколько ячеек, уменьшая contention. Итоговое значение вычисляется только при вызовеsum().private LongAdder adder = new LongAdder(); public void increment() { adder.increment(); // Минимизирует contention } public long get() { return adder.sum(); }
Рекомендация: Для частых обновлений и редких чтений используйте LongAdder. Для частых чтений и обновлений — AtomicLong.