Closed
Description
The following code can be found in org.springframework.data.redis.cache.RedisCache
.
private final Lock lock = new ReentrantLock();
@Override
@SuppressWarnings("unchecked")
public <T> T get(Object key, Callable<T> valueLoader) {
ValueWrapper result = get(key);
return result != null ? (T) result.get() : getSynchronized(key, valueLoader);
}
@Nullable
@SuppressWarnings("unchecked")
private <T> T getSynchronized(Object key, Callable<T> valueLoader) {
lock.lock();
try {
ValueWrapper result = get(key);
return result != null ? (T) result.get() : loadCacheValue(key, valueLoader);
} finally {
lock.unlock();
}
}
I believe there could be a significant amount of unnecessary locking happening here.
I don't think that this method should be locked via an instance-global ReentrantLock instance.
Instead, it should be locked on a per key basis, as follows:
private final ConcurrentHashMap<Object, Lock> keyToLock = new ConcurrentHashMap<>();
@Nullable
@SuppressWarnings("unchecked")
private <T> T getSynchronized(Object key, Callable<T> valueLoader) {
Lock lock = keyToLock.computeIfAbsent(key, k -> new ReentrantLock());
lock.lock();
try {
ValueWrapper result = get(key);
return result != null ? (T) result.get() : loadCacheValue(key, valueLoader);
} finally {
lock.unlock();
}
}
Plus additional cleanup calls to the ConcurrentHashMap for clear and evict calls to RedisCache.
What do you think?