Auswirkungen der Aktualisierung anderer Schlüssel in ConcurrentHashMap # computeIfAbsent

8

Javadoc von ConcurrentHashMap#computeIfAbsent sagt

  

Die Berechnung sollte kurz und einfach sein und darf nicht versuchen   Aktualisieren Sie alle anderen Zuordnungen dieser Karte.

Aber von dem, was ich sehe, funktioniert die Verwendung von remove() und clear() Methoden in mappingFunction gut. Zum Beispiel das

%Vor%

Welche negativen Konsequenzen hat die Verwendung der Methode remove () in mappingFunction ?

    
Vitalii Vitrenko 28.05.2017, 07:46
quelle

3 Antworten

4

Der Javadoc erklärt die Ursache klar:

  

Einige versuchte Aktualisierungsvorgänge in dieser Map durch andere Threads könnten sein    blockiert während der Berechnung , daher sollte die Berechnung erfolgen   kurz und einfach und darf nicht versuchen, irgendwelche anderen Abbildungen von zu aktualisieren   diese Karte.

Sie müssen nicht vergessen, dass ConcurrentHashMap eine Möglichkeit bietet, eine threadsichere Map zu verwenden, ohne so zu sperren, wie es bei älteren threadsicheren Map-Klassen wie HashTable der Fall ist.
Wenn eine Änderung auf der Karte auftritt, wird nur das betreffende Mapping und nicht die gesamte Map gesperrt.

  

ConcurrentHashMap ist eine Hash-Tabelle, die vollständige Parallelität von   Retrievals und hohe erwartete Parallelität für Updates.

computeIfAbsent() ist eine neue Methode, die in Java 8 hinzugefügt wurde.
Wenn sie schlecht verwendet wird, das heißt, wenn Sie im Körper von computeIfAbsent() bereits die Zuordnung des an die Methode übergebenen Schlüssels sperren, sperren Sie einen anderen Schlüssel, und geben Sie einen Pfad ein, in dem Sie den Zweck von ConcurrentHashMap as ablehnen können Schließlich werden Sie zwei Mapping sperren.
Stellen Sie sich das Problem vor, wenn Sie mehr Mapping in computeIfAbsent() sperren und die Methode nicht kurz ist. Concurrency-Zugriff auf der Karte würde langsam werden.

So betont das Javadoc von computeIfAbsent() dieses potentielle Problem, indem es die Prinzipien von ConcurrentHashMap erinnert: behalte es einfach und schnell.

Hier ist ein Beispielcode, der das Problem veranschaulicht.
Angenommen, wir haben eine Instanz von ConcurrentHashMap<Integer, String> .

Wir werden zwei Threads starten, die es verwenden:

  • Der erste Thread: thread1 , der computeIfAbsent() mit dem Schlüssel 1 aufruft
  • Der zweite Thread: thread2 , der computeIfAbsent() mit dem Schlüssel 2 aufruft

thread1 führt einen schnell genug Task aus, aber es folgt nicht die Angabe von computeIfAbsent() javadoc: es aktualisiert den Schlüssel 2 in computeIfAbsent() , das ist ein anderes Mapping von dem im aktuellen Kontext von die Methode (das ist Schlüssel 1 ).

thread2 führt eine ausreichend lange Aufgabe aus. Es ruft computeIfAbsent() mit dem Schlüssel 2 auf, indem es den Javadoc-Anweisungen folgt: Es aktualisiert keine anderen Mappings in der Implementierung Um die lange Aufgabe zu simulieren, können wir die Thread.sleep() -Methode mit 5000 als Parameter verwenden Wenn in dieser speziellen Situation thread2 vor thread1 beginnt, wird der Aufruf von map.put(2, someValue); in thread1 blockiert, während thread2 nicht von computeIfAbsent() zurückgegeben wird, wodurch die Zuordnung des Schlüssels 2 gesperrt wird.

Schließlich erhalten wir eine ConcurrentHashMap -Instanz, die die Zuordnung des Schlüssels 2 während 5 Sekunden blockiert, während computeIfAbsent() mit der Zuordnung des Schlüssels 1 aufgerufen wird.
Es ist irreführend, nicht effektiv und es geht gegen die Absicht ConcurrentHashMap und die computeIfAbsent() Beschreibung, welche die Absicht den Wert für den aktuellen Schlüssel berechnet:

  

wenn der angegebene Schlüssel nicht bereits mit einem Wert verknüpft ist, versucht   um seinen Wert unter Verwendung der gegebenen Abbildungsfunktion zu berechnen und sie einzugeben   in diese Karte, es sei denn, es ist null

Der Beispielcode:

%Vor%

Als Ausgabe erhalten wir immer:

  

thread1: get () gibt den Wert für Schlüssel 2 = null

zurück      

thread2: computeIfAbsent () gibt den Wert für Schlüssel 2 = zurück   valueSetByThread2

     

thread1: nach put () wird der vorherige Wert für Schlüssel 2 = zurückgegeben   valueSetByThread2

Dies wird schnell geschrieben, da die Lesevorgänge in ConcurrentHashMap nicht blockieren:

  

thread1: get () gibt den Wert für Schlüssel 2 = null

zurück

aber das:

  

thread1: nach put () wird der vorherige Wert für Schlüssel 2 = zurückgegeben   valueSetByThread2

wird nur ausgegeben, wenn Thread2 von computeIfAbsent() zurückgegeben wird.

    
davidxxx 28.05.2017, 08:42
quelle
5

Hier ist ein Beispiel für eine schlechte Konsequenz:

%Vor%

Dieser Code verursacht einen Deadlock.

    
Eran 28.05.2017 07:56
quelle
2

Solche Ratschläge sind ein bisschen wie der Rat, nicht mitten auf einer Straße zu gehen. Sie können es tun, und Sie werden vielleicht nicht von einem Auto angefahren; Sie können auch aus dem Weg gehen, wenn Sie das Auto kommen sehen.

Sie wären jedoch sicherer gewesen, wenn Sie nur auf dem Bürgersteig (Bürgersteig) geblieben wären.

Wenn ein API-Dokument Ihnen sagt, dass Sie etwas nicht tun sollen, hält Sie natürlich nichts davon ab. Und Sie könnten versuchen, es zu tun, und feststellen, dass es keine negativen Konsequenzen gibt, zumindest in den begrenzten Umständen, die Sie testen. Sie können sogar graben, um die genauen Gründe herauszufinden, warum der Rat da ist; Sie können den Quellcode überprüfen und beweisen, dass er in Ihrem Anwendungsfall sicher ist.

Allerdings steht es den Implementierern der API frei, die Implementierung innerhalb der durch die API-Dokumentation beschriebenen Einschränkungen des Vertrags zu ändern. Sie könnten eine Änderung vornehmen, die den Code morgen nicht mehr unterstützt, da sie nicht verpflichtet sind, Verhalten zu bewahren, das ausdrücklich vor der Verwendung gewarnt wird.

Also, um Ihre Frage zu beantworten, was die schlimmen Folgen sein könnten: buchstäblich alles (naja, alles, was normal abläuft oder ein RuntimeException wirft); und Sie würden nicht notwendigerweise die gleichen Konsequenzen im Laufe der Zeit oder auf verschiedenen JVMs beobachten.

Bleib auf dem Bürgersteig: Tu nicht, was dir die Dokumentation sagt.

    
Andy Turner 28.05.2017 07:56
quelle