Я хотел обсудить конкретное использование параллельной карты, чтобы проверить мою логику ...
Если бы я использовал ConcurrentHashMap
, Я могу выполнить знакомое
private final ConcurrentHashMap<K, V> map = new ConcurrentHashMap<K, V>();
public V getExampleOne(K key) {
map.putIfAbsent(key, new Object());
return map.get(key);
}
, но я понимаю, что существует условие гонки , при котором, если я удалю элемент с карты между putIfAbsent
и get
] , приведенный выше метод вернет то, чего больше нет в коллекции. Это может быть хорошо, а может и нет, но давайте предположим, что для моего варианта использования это не нормально.
Я действительно хотел бы, чтобы все это было атомарным. Итак,
public V getExampleTwo(K key) {
return map.putIfAbsent(key, new Object());
}
, но по мере расширения до
if (!map.containsKey(key))
return map.put(key, value); [1]
return map.get(key);
, который для строки [1] вернет null
для первого использования (т.е. map.put
вернет предыдущее значение, который при первом использовании равен null
).
Я не могу вернуть null в этом случае
Что оставляет мне что-то вроде:
public V getExampleThree(K key) {
Object object = new Object();
V value = locks.putIfAbsent(key, object);
if (value == null)
return object;
return value;
}
Итак, наконец, мой вопрос; как приведенные выше примеры отличаются семантикой ?. Обеспечивает ли getExampleThree
атомарность, как getExampleTwo
, но правильно ли избегает нулевого возврата? Есть ли другие проблемы с getExampleThree
?
Я немного надеялся обсуждения выбора.Я понимаю, что могу использовать не ConcurrentHashMap
и синхронизировать клиентов, вызывая мой метод get
и метод, который нужно удалить с карты, но это, похоже, нарушает цель (неблокирующий характер) ConcurrentHashMap. Неужели это единственный мой выбор, чтобы данные были точными ?
Думаю, это небольшая часть того, почему вы выбрали ConcurrentHashMap; что он виден / актуален / точен в момент, когда вы с ним взаимодействуете, но могут возникнуть последствия, если старые данные будут проблемой ...