Добавяне към AtomicInteger в ConcurrentHashMap - java, multithreading, concurrency, java.util.concurrent, concurrenthashmap

Имам следното определение

private ConcurrentMap<Integer, AtomicInteger>  = new ConcurrentHashMap<Integer, AtomicInteger>();

private void add() {
staffValues.replace(100, staffValues.get(100), new AtomicInteger(staffValues.get(100).addAndGet(200)));
}

След тестването ценностите, които получавам, не са такиваочаквам и мисля, че тук има условие за състезание. Някой знае ли, ако това ще се счита за защита от нишка чрез обвиване на получаване на повикване във функцията за замяна?

Отговори:

2 за отговор № 1

Има няколко проблема с вашия код. Най-голямото е, че пренебрегвате възвръщаемостта на ConcurrentHashMap.replace: ако замяната не се случи (поради друга нишка, която направи паралелно замяна), просто продължавате като че ли случи се. Това е основната причина да получите грешни резултати.

Също така мисля, че е грешка в дизайна, за да се промени мутацията AtomicInteger и веднага го заменете с друг AtomicInteger; дори ако можете да получите това, няма просто причина за това.

И накрая, не мисля, че трябва да се обадите staffValues.get(100) два пъти. Не мисля, че това причинява бъг в текущия код - вашата коректност зависи само от второто обаждане, което връща "по-нов" резултат от първия, което мисля е действително гарантирани от ConcurrentHashMap - но е крехка, фина и объркваща. По принцип, когато се обаждате ConcurrentHashMap.replace, неговият трети аргумент трябва да е нещо, което сте изчислили, като използвате втория.

Като цяло можете да опростите кода си, като не използвате AtomicInteger:

private ConcurrentMap<Integer, Integer> staffValues = new ConcurrentHashMap<>();

private void add() {
final Integer prevValue = staffValues.get(100);
staffValues.replace(100, prevValue, prevValue + 200);
}

или като не използвате replace (и може би дори не ConcurrentMap, в зависимост от това как иначе се докосваш до тази карта):

private Map<Integer, AtomicInteger> staffValues = new HashMap<>();

private void add() {
staffValues.get(100).addAndGet(200);
}

1 за отговор № 2

Един добър начин да се справите със ситуации като този е използването на computeIfAbsent метод (не compute метод, който @ the8472 препоръчва)

Най- computeIfAbsent приема два аргумента, клавиша и a Function<K, V> това ще бъде наречено само ако съществуващата стойност липсва. Тъй като AtomicInteger е безопасно за конеца от няколко теми, можете да го използвате по-лесно по следния начин:

staffValues.computeIfAbsent(100, k -> new AtomicInteger(0)).addAndGet(200);

0 за отговор № 3

Не е нужно да използвате replace(), AtomicInteger е заменяема стойност, която не трябва да бъде замествана, когато искате да я увеличите. Всъщност addAndGet вече го увеличава.

Вместо това използвайте compute да зададете стойност по подразбиране (вероятно 0) в картата, когато няма такава, и по друг начин да получите предварително съществуващата стойност и увеличение.

Ако, от друга страна, искате да използвате неизменни стойности Integer вместо AtomicInteger в картата и да ги актуализирате с операциите за атомно изчисление / замяна / сливане.


Свързани въпроси
Най - известен