Pytanie w sprawie concurrency, java, caching, hashmap – ConcurrentHashMap umieścić vs putIfAbsent

17

JawaDocs mówi że,putIfAbsent jest równa

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

Więc jeśli klucz istnieje na mapie, nie aktualizuje swojej wartości. Czy to jest poprawne?

Co zrobić, jeśli chcę zaktualizować wartość kluczy na podstawie pewnych kryteriów? Powiedz czas wygaśnięcia itp.

Czy byłoby to lepsze do dodawania i aktualizowania pamięci podręcznej?

<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>
Twoja logika jest jednak trochę nie na miejscu. Jeślilocal jest równevalue po prostu zaktualizowałeś mapę. Nie masz na myśli, jeśli to jestnie równyvalue ? Czyli coś już było na mapie? Brian Roach
Oh, widzę, przepraszam. Używasz słowa „aktualizacja”, aby oznaczyć, że nie tworzysz. Masz rację, przepraszam. Gray
Uważaj na semantykę tego fragmentu w Javadoc - zawsze zwracapoprzedni wartość pod tym kluczem. Marko Topolnik
@ Szara druga linia w równowadze. kod mówiący, że jeśli klucza nie ma w pamięci podręcznej, umieść go, w przeciwnym razie zwróć wartość. nie aktualizuje wartości, jeśli para wartości klucza jest już w pamięci podręcznej. DarthVader

Twoja odpowiedź

2   odpowiedź
11

To jest poprawne. Zwróci bieżącą wartość, która była już na mapie.

czy byłoby to lepsze do dodawania i aktualizowania pamięci podręcznej?

Kilka rzeczy usprawni twoją implementację.

1 Nie powinieneś używać putIfAbsent do testowania, jeśli istnieje, powinieneś go używać tylko wtedy, gdy chcesz się upewnić, że taki nie istniejeputIfAbsent. Zamiast tego powinieneś użyćmap.get przetestować jego istnienie (lub map.contains).

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

2. Zamiast wstawić będziesz chciał zastąpić, ponieważ sytuacja wyścigu może wystąpić, gdyif mogą być ocenione jako fałszywe przez dwa lub więcej wątków, w których jeden z dwóch (lub więcej) wątków zastąpi wstawienia drugiego wątku.

Zamiast tego możesz to zrobićzastąpić

Gdy wszystko zostanie powiedziane i zrobione, może wyglądać tak

<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>
@jtahlborn zaktualizowany. Pomyśl, że powinien to zrobić. John Vint
@Jtahlborn dzięki właśnie to zauważyłem :) John Vint
Ponadto nie musisz ponownie wywoływać get (), jeśli putIfAbsent () zwraca wartość inną niż null, możesz użyć tego, co właśnie otrzymałeś. potrzebujesz drugiego wywołania get (), jeśli nie powiedzie się zamiana. jtahlborn
Możesz zobaczyć, czy_cache.replace(key,local,value) nie powiedzie się, spróbuje ponownie, dopóki nie zostanie osiągnięta instrukcja powrotu. John Vint
5

Poza tym, choć jest to rozsądny pomysł, nie zadziała w „równoległy„środowisko. PowódputIfAbsent() dodano metodę polegającą na tym, że mapa mogła zarządzać atomowością operacji przy użyciu dowolnej podstawowej obsługi, której używa, aby operacje były bezpieczne dla wątków. W Twojej implementacji 2 różne osoby dzwoniące mogą zakończyć wzajemne przechodzenie na siebie (pierwsza zastępuje wygasłą wartość nową, a druga natychmiast zastępuje pierwszą nową drugą).

Często nie można wskazać faktu o sytuacji równoległej. :) +1 gia

Powiązane pytania