Почему нет параллельной хэш-карты с результатом операции? А каков вариант использования putIfAbsent?
Я углубился в параллелизм и действительно изо всех сил пытался понять некоторые проекты, используемые в параллельных структурах данных, то есть параллельной хэш-карте.
Есть некоторые заявления, которые я сделал, и вопросы, выделенные жирным шрифтом, пожалуйста, помогите мне подтвердить и ответить на них.
putIfAbsent
не может сказать исполняющему потоку, действительно ли он заменил ключ новым значением или принял существующее старое, потому что в обоих случаях он возвратит старое значение. Так в чем же смысл "putIfAbsent", поскольку в большинстве случаев использования мы сообщаем конечному клиенту / пользователю / о результате операции, я понимаю, что это может быть обобщением, но все же? Большинство из нас, вероятно, будут в восторге от Optional<Entry<K,V>> putIfAbsentAndReturnValueIfPut(K,V)
что-то в этом роде:-)?
Что я получу от putIfAbsent
атомарность? Мне все еще нужно использовать синхронизированный доступ, если я хочу сообщить о результате операции для конечного пользователя.
Представьте себе следующую структуру:
Map<String, Integer> map = new ConcurrentHashMap<>();
Outcome someMethod(String key, Integer value) {
Integer returnedValue = map.putIfAbsent(key, value);// ??? so how do I know if I updated the value?
map.get(key).equals(value); // even if I compare, I am not in synchronized context
}
Так что, если мой ключ UUID, при очень долгом запуске он может сгенерировать дубликат ключа для новой записи карты, как я могу убедиться, что если второй поток вызывает "put" после первого с тем же ключом:
- A. Операция происходит, поток пытается поставить.
- B. Результат операции известен, поэтому второй поток может попытаться сгенерировать новый UUID и повторить попытку ввода или уведомить вызывающую сторону о том, что операция завершилась неудачно?
Обновление: спасибо всем, это один из тех дней..
2 ответа
Вы всегда можете узнать, было ли значение добавлено или уже присутствовало, проверив возвращаемое значение putIfAbsent
:
final ConcurrentMap<String,Object> map = new ConcurrentHashMap<>();
final Object old = map.putIfAbsent("hello", "world");
if (old != null) {
// The value was already present and has not been modified
// by the call.
} else {
// There was no prior entry, and "world" has been assigned
// as value for key "hello"
}
Обратите внимание, что это все еще неоднозначно, если реализация карты принимает null
в качестве значения для записи. (Вероятно) по этой причине, ConcurrentHashMap
не принимает null
для ключей и значений:
Как и Hashtable, но в отличие от HashMap, этот класс не позволяет использовать null в качестве ключа или значения.
(как указано в документации)
" putIfAbsent
не может сказать исполняющему потоку, действительно ли он заменил ключ новым значением или он принял существующее старое, потому что в обоих случаях он возвратит старое значение."
Вы на самом деле читали Javadoc putIfAbsent()
? Кажется, вы читали Javadoc put()
и предположил, что putIfAbsent()
ведет себя так же. Это не:
Если указанный ключ еще не связан со значением (или сопоставлен со значением NULL), связывает его с данным значением и возвращает
null
, иначе возвращает текущее значение.
поскольку ConcurrentHashMap
не позволяет null
для использования в качестве ключа или значения, вы будете точно знать, добавил ли метод значение. null
означает добавленную стоимость, не null
означает, что значение не было добавлено, а возвращаемое значение - это значение, уже имеющееся на карте.
Окончательное определение результата!