2015-03-09 5 views
3

У меня вопрос о синхронизации объектов внутри Карты (те же объекты, которые я позже меняю). Я хочу, чтобы атомарно читал, делал проверки и, возможно, обновлял значение с карты без блокировки всей карты. Является ли это действенным способом работы с синхронизацией объектов?Синхронизированный объект объекта HashMap

private final Map<String, AtomicInteger> valueMap = new HashMap<>(); 

    public Response addValue(@NotNull String key, @NotNull Integer value) { 
     AtomicInteger currentValue = valueMap.get(key); 
     if (currentValue == null) { 
      synchronized (valueMap) { 
       // Doublecheck that value hasn't been changed before entering synchronized 
       currentValue = valueMap.get(key); 
       if (currentValue == null) { 
        currentValue = new AtomicInteger(0); 
        valueMap.put(key, currentValue); 
       } 
      } 
     } 
     synchronized (valueMap.get(key)) { 
      // Check that value hasn't been changed when changing synchronized blocks 
      currentValue = valueMap.get(key); 
      if (currentValue.get() + value > MAX_LIMIT) { 
       return OVERFLOW; 
      } 
      currentValue.addAndGet(value); 
      return OK; 
     } 
    } 
+3

Вы также можете использовать 'ConcurrentHashMap' или' Collections.synchronizedMap (map) '(см. [Этот вопрос] (http://stackoverflow.com/questions/510632/whats-the-difference-between-concurrenthashmap-and -collections-synchronizedmap)) для синхронизации вашей карты. – NiziL

+0

Как вы гарантируете, что значение не изменилось между проверкой get + до обновления/возврата в ConcurrentHashMap, например. Он должен работать для обновления, так как у вас будет сохранено и проверено предыдущее значение при обновлении, но при возврате OVERFLOW должна быть возможность изменения значения перед возвратом, если вы не заблокируете доступ между проверкой и возвратом? – AppX

+0

В вашем коде мне не ясно, что 'currentValue' не будет сохранять устаревшее значение, если доступ к' valueMap' будет выполняться без синхронизации (как вы это сделали). – scottb

ответ

1

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

В ConcurrentHashMap, вы бы использовать метод replace(K key, V old, V new) атомарно обновить key до new только тогда, когда значение old не изменилось.

Пространство Экономия за счет удаления всех этих AtomicIntegers и экономию времени из-за снижения накладных расходов синхронизации, вероятно, компенсирует того, чтобы обернуть replace(k, old, new) звонки внутри, а-петель:

ConcurrentHashMap<String, Integer> valueMap = 
     new ConcurrentHashMap<>(16, .75f, expectedConcurrentThreadCount); 

public Response addToKey(@NotNull String key, @NotNull Integer value) { 
    if (value > MAX_LIMIT) { 
     // probably should set value to MAX_LIMIT-1 before failing 
     return OVERFLOW; 
    } 
    boolean updated = false; 
    do { 
     Integer old = putIfAbsent(key, value); 
     if (old == null) { 
      // it was absent, and now it has been updated to value: ok 
      updated = true; 
     } else if (old + value > MAX_LIMIT) { 
      // probably should set value to MAX_LIMIT-1 before failing 
      return OVERFLOW; 
     } else { 
      updated = valueMap.replace(key, old, old+value); 
     } 
    } while (! updated); 

    return OK; 
} 

Кроме того, на положительной стороне, этот код работает, даже если ключ был удален после проверки (в этом случае вы выбрали NPE).

+1

'putIfAbsent()' не возвращает логическое значение. Кроме того, я не понимаю, почему вы используете 'get()', проверяя, возвращает ли он «null», а затем использует 'putIfAbsent()', в то время как вы можете отлично использовать 'putIfAbsent()' напрямую: если он вернется 'null', это означает, что значение не было на карте, иначе оно возвращает значение. –

+1

Истина по обоим пунктам, @Magnamag. Исправлена. – tucuxi

+0

Спасибо за ввод. Это кажется очень хорошим, за исключением получения начального значения> MAX_LIMIT. – AppX

Смежные вопросы