My doubt is simple. According to other posts this is not thread safe because of the reading+write operation count++. But a compute as far as I know, blocks the entire sector for that key inside the map. So it's impossible that two threads performs this operation at the same time. Why should I use atomic integer or any other concurrent object?
Key can be a profile and the counter it's just a counter for something that can be numeric. Posts, messages, likes... The structure is a ConcurrentHashMap<Key,Integer>.
counters.compute(key, (key, counter) -> {
if (counter == null) {
return 1;
} else {
return count++;
}
});
I didn't find any issue executing threads in some tests. But still I don't understand why should I use atomic integer.
Your example is flawed because return count++;
increments count
but returns the previous value - i.e. it never counts up. That line should be return count + 1;
:
counters.compute(key, (k, count) -> {
if (count == null) {
return 1;
} else {
return count+1;
}
});
Note that this usage of ConcurrentHashMap.compute()
(with Integer
as value) is inherently thread safe:
compute()
function is atomically executed for a specific key - i.e. no other thread can call compute()
for the same key at the same time and therefore you cannot loose an update.get()
calls either see the old or the new value depending on the exact timing but (since Integer
is immutable) they cannot corrupt the count.Using the merge()
method leads to an even simpler solution (thanks to David Conrad for the hint):
counters.merge(key, 1, Math::addExact);
This throws an ArithmeticException
if the counter would overflow. If you want to allow the counter to overflow you can use
counters.merge(key, 1, Integer::sum);