Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 435
Holger Avatar asked Apr 23 '12 11:04

Holger


People also ask

What is difference between computeIfAbsent and putIfAbsent?

computeIfAbsent returns "the current (existing or computed) value associated with the specified key, or null if the computed value is null". putIfAbsent returns "the previous value associated with the specified key, or null if there was no mapping for the key".

What is difference between synchronizedMap and ConcurrentHashMap?

synchronizedMap() requires each thread to acquire a lock on the entire object for both read/write operations. By comparison, the ConcurrentHashMap allows threads to acquire locks on separate segments of the collection, and make modifications at the same time.

Is ConcurrentHashMap computeIfAbsent thread-safe?

ConcurrentHashMap is one of the most frequently used collection classes in our daily work, Its characteristic is high performance and thread-safety. However, there are two bugs that impact the performance of this familiar map. The problem is in computeIfAbsent .

What is use of putIfAbsent?

The Java HashMap putIfAbsent() method inserts the specified key/value mapping to the hashmap if the specified key is already not present in the hashmap. Here, hashmap is an object of the HashMap class.


2 Answers

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;
    }
}
like image 140
assylias Avatar answered Oct 03 '22 05:10

assylias


Your method seems thread safe. If you do not require the performance benefits of ConcurrentHashMap, consider using a regular HashMap instead and synchronize all access to it. Your method is similar to AtomicInteger.getAndSet(int), so it should be fine. I doubt there is an easier way to do this unless you're looking for a library call to do the work for you.

like image 39
Nate Avatar answered Oct 03 '22 07:10

Nate