Атомарно увеличивающие счетчики сохранены в ConcurrentHashMap

Я хотел бы забрать некоторые метрики из различных мест в веб-приложении. Для хранения этого простым все, они будут счетчиками, и поэтому единственная операция модификатора должна увеличить их 1.

Инкременты будут параллельны и часто. Чтения (выводящий статистику) являются редкой операцией.

Я думал для использования ConcurrentHashMap. Проблема - то, как увеличить счетчики правильно. Так как карта не начинает "инкрементную" операцию, я должен считать текущее значение сначала, увеличить его, чем помещенный новое значение в карте. Без большего количества кода это не атомарная операция.

Действительно ли возможно достигнуть этого без синхронизации (который победил бы цель ConcurrentHashMap)? Я должен посмотреть на Гуаву?

Спасибо за любые указатели.


P.S.
Существует связанный вопрос на ТАК (Самый эффективный способ увеличить значение Карты в Java), но сфокусированный на производительности и не многопоточности

ОБНОВЛЕНИЕ
Для тех, которые прибывают здесь посредством поисков по той же теме: помимо ответов ниже, существует полезная презентация, которая случайно затрагивает ту же тему. Посмотрите слайды 24-33.

35
задан Community 23 May 2017 в 10:31
поделиться

2 ответа

Помимо использования AtomicLong , вы можете выполнить обычную операцию cas-loop:

private final ConcurrentMap<Key,Long> counts =
    new ConcurrentHashMap<Key,Long>();

public void increment(Key key) {
    if (counts.putIfAbsent(key, 1)) == null) {
        return;
    }

    Long old;
    do {
       old = counts.get(key);
    } while (!counts.replace(key, old, old+1)); // Assumes no removal.
}

(я не писал do - while цикл на века.)

Для малых значений Long , вероятно, будет «кэшироваться». Для более длинных значений может потребоваться выделение. Но выделения на самом деле очень быстрые (и вы можете кэшировать дальше) - в худшем случае зависит от того, чего вы ожидаете.

4
ответ дан 27 November 2019 в 07:06
поделиться

Вы довольно близки. Почему бы вам не попробовать что-то вроде ConcurrentHashMap? Если ваши Keyы (метрики) неизменны, вы даже можете просто использовать стандартный HashMap (они потокобезопасны, если доступны только для чтения, но лучше сделать это явным с помощью ImmutableMap из Google Collections или Collections.unmodifiableMap и т.д.).

Таким образом, вы можете использовать map.get(myKey).incrementAndGet() для получения статистики.

8
ответ дан 27 November 2019 в 07:06
поделиться