|
|
|
@ -57,12 +57,12 @@ public class LFUCacheMap<K, V> extends AbstractCacheMap<K, V> {
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static class LFUCachedValue extends StdCachedValue<Object, Object> {
|
|
|
|
|
public static class LFUCachedValue<K, V> extends StdCachedValue<K, V> {
|
|
|
|
|
|
|
|
|
|
private final Long id;
|
|
|
|
|
private long accessCount;
|
|
|
|
|
|
|
|
|
|
public LFUCachedValue(long id, Object key, Object value, long ttl, long maxIdleTime) {
|
|
|
|
|
public LFUCachedValue(long id, K key, V value, long ttl, long maxIdleTime) {
|
|
|
|
|
super(key, value, ttl, maxIdleTime);
|
|
|
|
|
this.id = id;
|
|
|
|
|
}
|
|
|
|
@ -74,46 +74,47 @@ public class LFUCacheMap<K, V> extends AbstractCacheMap<K, V> {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private final AtomicLong idGenerator = new AtomicLong();
|
|
|
|
|
private final ConcurrentNavigableMap<MapKey, LFUCachedValue> accessMap = new ConcurrentSkipListMap<MapKey, LFUCachedValue>();
|
|
|
|
|
private final ConcurrentNavigableMap<MapKey, LFUCachedValue<K, V>> accessMap = new ConcurrentSkipListMap<>();
|
|
|
|
|
|
|
|
|
|
public LFUCacheMap(int size, long timeToLiveInMillis, long maxIdleInMillis) {
|
|
|
|
|
super(size, timeToLiveInMillis, maxIdleInMillis);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
protected CachedValue create(K key, V value, long ttl, long maxIdleTime) {
|
|
|
|
|
return new LFUCachedValue(idGenerator.incrementAndGet(), key, value, ttl, maxIdleTime);
|
|
|
|
|
protected CachedValue<K, V> create(K key, V value, long ttl, long maxIdleTime) {
|
|
|
|
|
return new LFUCachedValue<K, V>(idGenerator.incrementAndGet(), key, value, ttl, maxIdleTime);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
protected void onValueCreate(CachedValue value) {
|
|
|
|
|
MapKey key = toKey((LFUCachedValue) value);
|
|
|
|
|
accessMap.put(key, (LFUCachedValue) value);
|
|
|
|
|
protected void onValueCreate(CachedValue<K, V> value) {
|
|
|
|
|
MapKey key = toKey((LFUCachedValue<K, V>) value);
|
|
|
|
|
accessMap.put(key, (LFUCachedValue<K, V>) value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
protected void onValueRead(CachedValue value) {
|
|
|
|
|
addAccessCount((LFUCachedValue) value, 1);
|
|
|
|
|
protected void onValueRead(CachedValue<K, V> value) {
|
|
|
|
|
addAccessCount((LFUCachedValue<K, V>) value, 1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private MapKey toKey(LFUCachedValue value) {
|
|
|
|
|
private MapKey toKey(LFUCachedValue<K, V> value) {
|
|
|
|
|
return new MapKey(value.accessCount, value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
protected void onValueRemove(CachedValue value) {
|
|
|
|
|
synchronized (value) {
|
|
|
|
|
MapKey key = toKey((LFUCachedValue) value);
|
|
|
|
|
protected void onValueRemove(CachedValue<K, V> value) {
|
|
|
|
|
value.getLock().execute(() -> {
|
|
|
|
|
MapKey key = toKey((LFUCachedValue<K, V>) value);
|
|
|
|
|
accessMap.remove(key);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void addAccessCount(LFUCachedValue value, long count) {
|
|
|
|
|
synchronized (value) {
|
|
|
|
|
private void addAccessCount(LFUCachedValue<K, V> value, long c) {
|
|
|
|
|
value.getLock().execute(() -> {
|
|
|
|
|
long count = c;
|
|
|
|
|
if (count < 0 && value.accessCount == 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
MapKey key = toKey(value);
|
|
|
|
|
if (accessMap.remove(key) == null) {
|
|
|
|
|
return;
|
|
|
|
@ -123,15 +124,15 @@ public class LFUCacheMap<K, V> extends AbstractCacheMap<K, V> {
|
|
|
|
|
count = -Math.min(value.accessCount, -count);
|
|
|
|
|
}
|
|
|
|
|
value.addAccessCount(count);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
key = toKey(value);
|
|
|
|
|
accessMap.put(key, value);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
protected void onMapFull() {
|
|
|
|
|
Map.Entry<MapKey, LFUCachedValue> entry = accessMap.pollFirstEntry();
|
|
|
|
|
Map.Entry<MapKey, LFUCachedValue<K, V>> entry = accessMap.pollFirstEntry();
|
|
|
|
|
if (entry == null) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
@ -143,7 +144,7 @@ public class LFUCacheMap<K, V> extends AbstractCacheMap<K, V> {
|
|
|
|
|
|
|
|
|
|
// TODO optimize
|
|
|
|
|
// decrease all values
|
|
|
|
|
for (LFUCachedValue value : accessMap.values()) {
|
|
|
|
|
for (LFUCachedValue<K, V> value : accessMap.values()) {
|
|
|
|
|
addAccessCount(value, -entry.getValue().accessCount);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|