Question

I am trying to track down a race condition and all signs seem to be pointing to ConcurrentHashMap.putIfAbsent(). Is it possible that if 2 threads call putIfAbsent() on an empty map with the same key that both could do their lookup to see that the key does not exist yet so both threads then try to add it? For some reason when I first started using putIfAbsent() I did not think the call would need to be synchronized. But now I can't see how it would prevent both threads from adding their values if the timing was right. I have not been able to reproduce this outside of production.

Thanks

Was it helpful?

Solution

None of the operations for any concurrent collection needs to use synchronized.

This is by design and in fact locking the collection has no effect on other operations. (Unless they are locked as well) In which case it will make them slower.

Is it possible that if 2 threads call putIfAbsent() on an empty map with the same key that both could do their lookup to see that the key does not exist yet so both threads then try to add it?

Both can try, but only one will succeed. It is not possible for two threads to appear to have succeeded.

For some reason when I first started using putIfAbsent() I did not think the call would need to be synchronized.

It doesn't.

But now I can't see how it would prevent both threads from adding their values if the timing was right.

It performs a CAS operation in the code which means only one operation can succeed and the thread will know which one. A CAS operation doesn't need locking as it uses the underlying assembly instruction to perform this. In fact you would normally implement a lock using a CAS operation, rather than the other way around.

OTHER TIPS

Is it possible that if 2 threads call putIfAbsent on an empty map with the same key that both could do their lookup to see that the key does not exist yet so both threads then try to add it?

Not according to the documentation for putIfAbsent():

If the specified key is not already associated with a value, associate it with the given value. This is equivalent to

if (!map.containsKey(key))
    return map.put(key, value);
else
    return map.get(key);

except that the action is performed atomically.

This means that is not possible for both threads to attempt to insert the key-value pair.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top