12

Dies kann eine doppelte Frage sein, aber ich habe diesen Teil des Codes in einem Buch über Nebenläufigkeit gefunden. Dies soll die Threadsicher:Java ConcurrentHashMap Aktionen Atomizität

ConcurrentHashMap<String, Integer> counts = new ...; 

private void countThing(String thing) { 
    while (true) { 
     Integer currentCount = counts.get(thing); 
     if (currentCount == null) { 
      if (counts.putIfAbsent(thing, 1) == null) 
       break; 
     } else if (counts.replace(thing, currentCount, currentCount + 1)) { 
      break; 
     } 
    } 
} 

Aus meiner (Concurrency Anfänger) Sicht Gewinde t1 und Faden t2 beide currentCount = 1 lesen konnte. Dann könnten beide Threads den Wert der Karte auf 2 ändern. Kann mir bitte jemand erklären, ob der Code in Ordnung ist oder nicht?

Antwort

11

Der Trick ist, dass replace(K key, V oldValue, V newValue) die Atomarität für Sie bereitstellt. Von the docs (Hervorhebung von mir):

Ersetzt den Eintrag für einen Schlüssel nur wenn zur Zeit auf einen bestimmten Wert zugeordnet. ... die Aktion wird atomar ausgeführt.

Das Schlüsselwort ist "atomar". Innerhalb replace, die "überprüfen, ob der alte Wert ist, was wir erwarten, und nur wenn es ist, ersetze es" geschieht als ein einziger Teil der Arbeit, mit keine anderen Threads in der Lage, damit zu verschachteln. Es liegt an der Implementierung, die erforderliche Synchronisierung durchzuführen, um sicherzustellen, dass sie diese Atomarität bereitstellt.

So kann es nicht sein, dass beide Threads currentAction == 1 aus der replace Funktion sehen. Einer von ihnen wird es als 1 sehen, und somit wird sein Aufruf an replace wahr zurückkehren. Der andere wird es als 2 (wegen des ersten Anrufs) sehen und daher false — zurückgeben und es erneut versuchen, diesmal mit dem neuen Wert currentAction == 2.

Natürlich könnte es sein, dass ein dritter Thread in der Zwischenzeit currentAction auf 3 aktualisiert hat. In diesem Fall wird der zweite Thread einfach weiter versuchen, bis er das Glück hat, dass niemand vor ihm springt.

+0

Sie in Java hinzugefügt wurde vielen Dank, ich glaube, ich verstehe es jetzt. :) –

-1

Mit Put können Sie auch den Wert ersetzen.

if (currentCount == null) { 
     counts.put(thing, 2); 
    } 
6

Kann mir bitte jemand erklären, wenn der Code in Ordnung ist oder nicht?

Neben yshavit Antwort, können Sie vermeiden, indem Sie compute Ihre eigene Schleife schreiben, die 8.

ConcurrentMap<String, Integer> counts = new ...; 

private void countThing(String thing) { 
    counts.compute(thing, (k, prev) -> prev == null ? 1 : 1 + prev); 
} 
+0

Cool, danke! :) –