I have a ConcurrentMap<Key, Long>
that is a cache from some key to the greatest value for it that has been seen so far. If two values come in at once, I want to ensure that the larger one is inserted into the map. What's the cleanest way to do this?
One ugly way is to write a class LargestLong
with an UpdateIfBigger
method, that will probably involve some CAS loop. I don't see a way to do such a CAS loop on a ConcurrentHashMap
directly.
Or use a ConcurrentMap<Key, AtomicLong>
and a CAS loop after retrieval. I've heard it's usually a sign of a design flaw to use a concurrent collection against an atomic primitive like this, but this may be an exception.
Ideally, you should start with a simple synchronized block, identify that it indeed creates a bottle neck, then try to do it lock free. Maybe more complicated solution is not worth the effort or you have a bottle neck elsewhere?
John's solution seems faster but, if this is critical section of your program, you might want to also test the following (using just ConcurrentMap's atomic methods):
ConcurrentMap<Key, Long> map = new ConcurrentHashMap<>();
public boolean putIfGreater(Key key, long value) {
Long boxedValue = Long.valueOf(value);
Long current = map.putIfAbsent(key, boxedValue);
if (current == null) {
return true;
} else {
while (true) {
if (value < current) {
return false;
}
if (map.replace(key, current, boxedValue)) {
return true;
}
current = map.get(key);
}
}
}