Search code examples
javaconcurrencythread-safety

Threadsafe "put if greater" in map method


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.


Solution

  • 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);
            }
        }
    }