Search code examples
javaconcurrencysynchronizationconcurrenthashmap

combine putIfAbsent and replace with ConcurrentMap


I have a usecase where I have to

  • insert a new value if the key does not exist in the ConcurrentHashMap
  • replace the old value with a new value if the key already exists in the ConcurrentHashMap, where the new value is derived from the old value (not an expensive operation)

I've the following code to offer:

public void insertOrReplace(String key, String value) {
        boolean updated = false;
        do {
            String oldValue = concurrentMap.get(key);
            if (oldValue == null) {
                oldValue = concurrentMap.putIfAbsent(key, value);
                if (oldValue == null) {
                    updated = true;
                }
            }
            if (oldValue != null) {
                final String newValue = recalculateNewValue(oldValue, value);
                updated = concurrentMap.replace(key, oldValue, newValue);
            }
        } while (!updated);
    }

Do you think it's correct and thread-safe?

Is there a simpler way?


Solution

  • You could make it a little shorter with the code below which is equivalent to yours. I have stress tested it a little with thousands of threads accessing it concurrently: it works as expected, with a number of retries (loops) being performed (obviously, you can never prove correctness with testing in the concurrent world).

    public void insertOrReplace(String key, String value) {
        for (;;) {
            String oldValue = concurrentMap.putIfAbsent(key, value);
            if (oldValue == null)
                return;
    
            final String newValue = recalculateNewValue(oldValue, value);
            if (concurrentMap.replace(key, oldValue, newValue))
                return;
        }
    }