Difference in semantics of computeIfAbsent between OpenJDK and Oracle JDK

Viewed 85

I've noticed a serious difference in the semantics of the ConcurrentHashMap.computeIfAbsent method between Oracle's JDK and Open JDK. This is surprising, so I wanted to make note of it, and see whether others have noticed this issue, or if I'm misinterpreting something.

According to the Javadoc for Oracle's JDK (beginning with v8, and continuing to v15) the semantics for invocation of the mappingFunction are:

The entire method invocation is performed atomically, so the function is applied at most once per key.

However, I noticed the function being invoked twice in my program when the key was accessed simultaneously from two different threads. Digging deeper, I noticed that Open JDK's documentation specifies different semantics:

The entire method invocation is performed atomically. The supplied function is invoked exactly once per invocation of this method if the key is absent, else not at all.

That's quite a big difference in behavior, and consistent with what I observed in my program. Open JDK's implementation is far less useful, in my opinion, because the resource being constructed is oftentimes very expensive and it should be created at most once.

1 Answers

That's quite a big difference in behavior

There is no behavior difference. They behave exactly the same.

The wording of the javadoc has simply been changed.

The entire method invocation is performed atomically, so the function is applied at most once per key.

vs:

The entire method invocation is performed atomically. The supplied function is invoked exactly once per invocation of this method if the key is absent, else not at all.

If two threads are in a race condition for the same key, each thread calls computeIfAbsent, so two invocations of the method may occur, one invocation per thread, i.e. one invocation of the function per call of computeIfAbsent, as described in the second version.

The atomic guarantee means that only one thread applies the value to the map, i.e. the result of the function is only applied once for the key in question, as described in the first version. The result of the function call made by the other thread is simply discarded.

As you can see, both versions of the javadoc description accurately describes the behavior of the method. The descriptions don't contradict each other, as they describe two different aspects of the behavior.

Related