Счетчик одновременных частот - проблема параллелизма - PullRequest
0 голосов
/ 27 апреля 2019

Я хотел бы создать класс параллельного счетчика частоты в Java.

Речь идет о том, что после обработки запроса (методом processRequest) код проверяет тип запроса (целое число) и подсчитывает, сколькозапросы были обработаны (сгруппированы по типу запроса) с заданного времени.Метод processRequest будет вызываться несколькими потоками одновременно.

Есть два других метода:

  • clearMap (): он будет вызываться одним потоком каждые 3 часаи очищает всю карту.
  • getMap (): он может быть вызван в любое время веб-службой и возвращает неизменную копию текущего состояния карты частот.

См.ниже моего первоначального плана по реализации этого.

public class FrequencyCounter {

     private final ConcurrentHashMap<Integer,Long> frequencenyMap = new ConcurrentHashMap<>();

     public void processRequest(Request request){
         frequencenyMap.merge(request.type, 0L, (v, d) -> v+1);
     }

     public void clearMap(){
         frequencenyMap.clear();
     }

     public Map<Integer,Long> getMap(){
         return ImmutableMap.copyOf(frequencenyMap);
     }
}

Я проверил документацию ConcurrentHashMap и он сообщает, что метод слияния выполняется атомарно.

Так что, как только метод clear () начинает очищатьсяхэш-сегменты карты (блокировка в соответствии с хэш-сегментом), его нельзя вызвать, когда другой поток находится между получением значения карты частот и увеличением его значения в методе processRequest, поскольку метод слияния выполняется атомарно.

Я прав?Мой план выше, кажется, в порядке?

Спасибо за ваш совет.

1 Ответ

0 голосов
/ 27 апреля 2019

Сначала замените Long на AtomicLong.

Во-вторых, используйте computeIfAbsent.

 private final Map<Integer, AtomicLong> frequencyMap = new ConcurrentHashMap<>();

 public void processRequest(Request request){
     frequencyMap.computeIfAbsent(request.type, k -> new AtomicLong())
                 .incrementAndGet();
 }

ЕстьВот несколько причин, почему я считаю, что это лучшее решение:

  1. Код в вопросе использует объекты в штучной упаковке, т.е. (v, d) -> v+1 действительно (Long v, Long d) -> Long.valueOf(v.longValue() + 1).

    код генерирует дополнительный мусор, которого можно избежать, используя AtomicLong.

    . Код здесь выделяет только один объект на ключ и не требует никаких дополнительных выделений для увеличения счетчика, например, он все еще будетодин объект, даже если счетчик идет в миллионы.

  2. Операция распаковки, добавление 1, скорее всего, займет немного больше времени, чем жестко закодированная операция incrementAndGet(), увеличивая вероятностьстолкновение, требующее повторной попытки в методе merge.

  3. Код "чистота".Использование метода, который принимает «значение», которое затем полностью игнорируется, кажется мне неправильным.Это ненужный шум кода.

Это, конечно, мое мнение.Вы можете принять собственное решение, но я думаю, что этот код проясняет цель, то есть увеличить счетчик long, полностью поточно-ориентированным способом.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...