package de.invesdwin.util.collections.loadingcache.internal;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.Function;
import javax.annotation.concurrent.GuardedBy;
import javax.annotation.concurrent.ThreadSafe;
import de.invesdwin.util.collections.loadingcache.ILoadingCache;
@ThreadSafe
public abstract class ASynchronizedLoadingCache<K, V> implements ILoadingCache<K, V> {
@GuardedBy("this")
protected final Map<K, V> map;
private final Function<K, V> loadValue;
public ASynchronizedLoadingCache(final Function<K, V> loadValue, final Map<K, V> map) {
this.loadValue = loadValue;
this.map = map;
}
@Override
public V get(final K key) {
V v;
synchronized (this) {
v = map.get(key);
}
if (v == null) {
//bad idea to synchronize in apply, this might cause deadlocks when threads are used inside of it
v = loadValue.apply(key);
if (v != null) {
synchronized (this) {
final V oldV = map.get(key);
if (oldV != null) {
v = oldV;
} else {
map.put(key, v);
}
}
}
}
return v;
}
@Override
public synchronized void clear() {
map.clear();
}
@Override
public synchronized boolean containsKey(final K key) {
return map.containsKey(key);
}
@Override
public synchronized void remove(final K key) {
map.remove(key);
}
@Override
public synchronized void put(final K key, final V value) {
map.put(key, value);
}
@Override
public synchronized Set<Entry<K, V>> entrySet() {
return map.entrySet();
}
@Override
public synchronized int size() {
return map.size();
}
@Override
public synchronized Set<K> keySet() {
return map.keySet();
}
@Override
public synchronized Collection<V> values() {
return map.values();
}
@Override
public synchronized Map<K, V> asMap() {
return Collections.unmodifiableMap(map);
}
@Override
public synchronized boolean isEmpty() {
return map.isEmpty();
}
}