Frage an concurrency, caching, java, hashmap – ConcurrentHashMap put vs putIfAbsent

17

JavaDocs sagt, dass,putIfAbsent ist äquivalent zu

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

Wenn der Schlüssel in der Karte vorhanden ist, wird sein Wert nicht aktualisiert. Ist das richtig?

Was ist, wenn ich einen Schlüsselwert basierend auf bestimmten Kriterien aktualisieren möchte? Sagen Sie Ablaufzeit usw.

Wäre dies ein besseres Werkzeug zum Hinzufügen und Aktualisieren des Caches?

<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 ich verstehe, sorry. Sie verwenden das Wort "Update", um "nicht erstellen" zu bedeuten. Sie sind richtig, tut mir leid. Gray
Seien Sie sehr vorsichtig mit der Semantik dieses Snippets in Javadoc - es gibt immer das zurückBisherige Wert unter diesem Schlüssel. Marko Topolnik
Deine Logik ist allerdings etwas abgespritzt. oblocal entsprichtvalue dann hast du gerade die karte aktualisiert. Meinst du nicht, wenn es ist?nicht gleichvalue ? Bedeutet das, dass die Karte bereits etwas enthält? Brian Roach
@ Graue zweite Zeile in der Äquiv. Code, der besagt, dass der Schlüssel, wenn er nicht im Cache ist, dort abgelegt wird, andernfalls wird nur der Wert zurückgegeben. Der Wert wird nicht aktualisiert, wenn sich das Schlüsselwertpaar bereits im Cache befindet. DarthVader

Deine Antwort

2   die antwort
11

Daher wird der Wert eines Schlüssels nicht aktualisiert. ist das richtig?

Das ist richtig. Es wird der aktuelle Wert zurückgegeben, der bereits in der Karte vorhanden war.

Wäre dies ein besseres Werkzeug zum Hinzufügen und Aktualisieren des Caches?

Ein paar Dinge würden Ihre Implementierung verbessern.

1. Sie sollten putIfAbsent nicht verwenden, um zu testen, ob es vorhanden ist. Sie sollten es nur verwenden, wenn Sie sicherstellen möchten, dass es nicht vorhanden istputIfAbsent. Stattdessen sollten Sie verwendenmap.get um seine Existenz zu testen (oder map.contains).

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

2. Anstatt zu ersetzen möchten Sie, weil eine Racebedingung auftreten kann, wo dieif kann von zwei oder mehr Threads als falsch bewertet werden, wobei einer der zwei (oder mehr) Threads die Puts des anderen Threads überschreibt.

Was Sie stattdessen tun können, istersetzen

Wenn alles gesagt und getan ist, könnte es so aussehen

<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>
Es hätte ausgetauscht werden könnenwhile(true){  Aber was Sie versuchen, ist ein atomares Update. Es besteht die Wahrscheinlichkeit, dass eine Rennbedingung vorliegt, bei der eine Aktualisierung des aktuellen Werts im Cache (aufgrund mehrerer Puts gleichzeitig) fehlschlagen kann. Dasfor(;;){ simple sagt "versuche es weiter, bis du erfolgreich bist". for (;;) bedeutet einfach: Endlosschleife. John Vint
Sie können sehen, ob die_cache.replace(key,local,value) fehlschlägt, wird es erneut versuchen, bis eine return-Anweisung erreicht ist. John Vint
@jtahlborn Ich dachte darüber nach aufzuräumen, ich werde etwas hineinstecken. John Vint
@jtahlborn aktualisiert. Denke, das sollte es tun. John Vint
5

Ihr Code wird eine NPE auslösen, wenn der Schlüssel nicht zuvor in der Karte enthalten war.

Anders als das, obwohl dies eine vernünftige Idee ist, wird es nicht funktionieren in einem "gleichzeitig"Umwelt. Der Grund derputIfAbsent() Es wurde eine Methode hinzugefügt, mit der die Zuordnung die Atomizität des Vorgangs mithilfe der zugrunde liegenden Unterstützung verwalten kann, die verwendet wird, um den Vorgang threadsicher zu machen. In Ihrer Implementierung könnten zwei verschiedene Aufrufer aufhören, aufeinander zu treten (der erste ersetzt einen abgelaufenen Wert durch einen neuen und der zweite ersetzt sofort den ersten neuen durch einen zweiten neuen).

Auf die gleichzeitige Situation kann nicht oft genug hingewiesen werden. :) +1 gia

Verwandte Fragen