Я хотел бы забрать некоторые метрики из различных мест в веб-приложении. Для хранения этого простым все, они будут счетчиками, и поэтому единственная операция модификатора должна увеличить их 1.
Инкременты будут параллельны и часто. Чтения (выводящий статистику) являются редкой операцией.
Я думал для использования ConcurrentHashMap. Проблема - то, как увеличить счетчики правильно. Так как карта не начинает "инкрементную" операцию, я должен считать текущее значение сначала, увеличить его, чем помещенный новое значение в карте. Без большего количества кода это не атомарная операция.
Действительно ли возможно достигнуть этого без синхронизации (который победил бы цель ConcurrentHashMap)? Я должен посмотреть на Гуаву?
Спасибо за любые указатели.
P.S.
Существует связанный вопрос на ТАК (Самый эффективный способ увеличить значение Карты в Java), но сфокусированный на производительности и не многопоточности
ОБНОВЛЕНИЕ
Для тех, которые прибывают здесь посредством поисков по той же теме: помимо ответов ниже, существует полезная презентация, которая случайно затрагивает ту же тему. Посмотрите слайды 24-33.
Помимо использования 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
, вероятно, будет «кэшироваться». Для более длинных значений может потребоваться выделение. Но выделения на самом деле очень быстрые (и вы можете кэшировать дальше) - в худшем случае зависит от того, чего вы ожидаете.
Вы довольно близки. Почему бы вам не попробовать что-то вроде ConcurrentHashMap
?
Если ваши Key
ы (метрики) неизменны, вы даже можете просто использовать стандартный HashMap
(они потокобезопасны, если доступны только для чтения, но лучше сделать это явным с помощью ImmutableMap
из Google Collections или Collections.unmodifiableMap
и т.д.).
Таким образом, вы можете использовать map.get(myKey).incrementAndGet()
для получения статистики.