Quesion about ConcurrentMap.getOrPut

Hi,

I have a question about ConcurrentMap.getOrPut function. How could it happen that
but the [defaultValue] function may be invoked even if the key is already in the map?

/**
 * Concurrent getOrPut, that is safe for concurrent maps.
 *
 * Returns the value for the given [key]. If the key is not found in the map, calls the [defaultValue] function,
 * puts its result into the map under the given key and returns it.
 *
 * This method guarantees not to put the value into the map if the key is already there,
 * but the [defaultValue] function may be invoked even if the key is already in the map.
 */
public inline fun <K, V> ConcurrentMap<K, V>.getOrPut(key: K, defaultValue: () -> V): V {
    // Do not use computeIfAbsent on JVM8 as it would change locking behavior
    return this.get(key)
            ?: defaultValue().let { default -> this.putIfAbsent(key, default) ?: default }

}

I, in facts, came across this problem, the defaultValue function was invoked repeatedly when the key was in the map. But I can not figure it out. Finally I change the function to code:

fun myGetOrPut():Item {
  val cacheItem = cacheMap[id]
  if (cacheItem!=null) return cacheItem
  val item = defaultValue()
  cacheMap[id] = item
  return item
}

And these code run well, defaultValue was never invoked when the key was there

Concurrency.

Another thread set the same id while your thread is invoking defaultValue(), then your thread replaces the value of other thread.
At the end different threads are working with different values.

2 Likes