Почему нет параллельной хэш-карты с результатом операции? А каков вариант использования 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 означает, что значение не было добавлено, а возвращаемое значение - это значение, уже имеющееся на карте.

Окончательное определение результата!

Другие вопросы по тегам