Pergunta sobre java, hashmap, concurrency, caching – ConcurrentHashMap put vs putIfAbsent

17

JavaDocs diz que,putIfAbsent é equivalente a

<code>   if (!map.containsKey(key)) 
      return map.put(key, value);
   else
      return map.get(key);
</code>

Portanto, se a chave existir no mapa, ela não atualizará seu valor. Isso está correto?

E se eu quiser atualizar um valor de chaves com base em alguns critérios? Diga o tempo de expiração etc.

Isso seria um melhor impl para adicionar e atualizar o cache?

<code>public void AddToCache(T key, V value)
{
   V local = _cache.putifabsent(key, value);

   if(local.equals(value) && local.IsExpired() == false){
     return;
   }
   // this is for updating the cache with a new value
   _cache.put(key, value);
}
</code>
Oh eu vejo, desculpe. Você está usando a palavra "atualização" para não criar. Você está correto, desculpe. Gray
Tenha muito cuidado com a semântica desse snippet no Javadoc - ele sempre retorna oanterior valor sob essa chave. Marko Topolnik
@Gray segunda linha no equiv. código dizendo que se a chave não está no cache, então coloque-o, caso contrário, basta retornar o valor. ele não atualiza o valor se o par de valores-chave já estiver no cache. DarthVader
sua lógica é um pouco hosed embora. E selocal é igual avalue então você acabou de atualizar o mapa. Você não quer dizer se énão igual avalue ? Significa que já havia algo no mapa? Brian Roach

Sua resposta

2   a resposta
5

Fora isso, embora esta seja uma idéia razoável, não vai funcionar em um "concorrente"ambiente. A razão pela qual oputIfAbsent() O método foi adicionado para que o mapa pudesse gerenciar a atomicidade da operação usando qualquer suporte subjacente que esteja usando para tornar as operações seguras. Na sua implementação, dois chamadores diferentes poderiam terminar um com o outro (o primeiro substitui um valor expirado por um novo e o segundo substitui imediatamente o primeiro por um novo).

O fato sobre a situação concorrente não pode ser apontado com freqüência suficiente. :) +1 gia
11

Está correto. Ele retornará o valor atual que já estava no mapa.

Isso seria um melhor impl para adicionar e atualizar o cache?

Algumas coisas tornariam sua implementação melhor.

1 Você não deve usar o putIfAbsent para testar se ele existe, você deve usá-lo somente quando quiser garantir que ele não existaputIfAbsent. Em vez disso você deve usarmap.get para testar sua existência (ou map.contains).

<code>    V local = _cache.get(key);
    if (local.equals(value) && !local.IsExpired()) {
        return;
    }
</code>

2 Em vez de colocar você vai querer substituir, isso é porque uma condição de corrida pode ocorrer onde oif pode ser avaliado como falso por dois ou mais encadeamentos nos quais um dos dois (ou mais) encadeamentos sobrescreverá os envios do outro encadeamento.

O que você pode fazer ésubstituir

Quando tudo estiver dito e feito, poderia ficar assim

<code>public void AddToCache(T key, V value) {
    for (;;) {

        V local = _cache.get(key);
        if(local == null){
            local = _cache.putIfAbsent(key, value);
            if(local == null)
                return;
        }
        if (local.equals(value) && !local.IsExpired()) {
            return;
        }

        if (_cache.replace(key, local, value))
            return;
    }
}
</code>
por que você temfor (;;) { ? qual é o ponto de interesse? DarthVader
Poderia ter sido trocado porwhile(true){  Mas o que você está tentando fazer é uma atualização atômica. Existe a probabilidade de uma condição de corrida na qual uma atualização para o valor atual no cache (como resultado de várias colocações ao mesmo tempo) pode falhar. ofor(;;){ simples diz "continue tentando até conseguir". para (;;) simplesmente significa loop para sempre. John Vint
Você pode ver se o_cache.replace(key,local,value) falhar, ele tentará novamente até que uma declaração de retorno seja atingida. John Vint
@jtahlborn obrigado só notei que eu mesmo :) John Vint

Perguntas relacionadas