com.kolibrifx.plovercrest.server.internal.engine.LeastRecentlyUsedCache Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of plovercrest-server Show documentation
Show all versions of plovercrest-server Show documentation
Plovercrest server library.
The newest version!
/*
* Copyright (c) 2010-2017, KolibriFX AS. Licensed under the Apache License, version 2.0.
*/
package com.kolibrifx.plovercrest.server.internal.engine;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.log4j.Logger;
public class LeastRecentlyUsedCache {
private static final Logger log = Logger.getLogger(LeastRecentlyUsedCache.class);
private final Map> cacheEntries = new ConcurrentHashMap<>();
private final int maxSize;
private final AtomicLong ageCounter = new AtomicLong();
private final Object putLock = new Object();
public LeastRecentlyUsedCache(final int maxSize) {
this.maxSize = maxSize;
}
public void add(final CacheEntry entry) {
if (!cacheEntries.containsKey(entry.getKey())) {
// Locking here prevents the cache from becoming too large. (sigh)
synchronized (putLock) {
while (cacheEntries.size() >= maxSize) {
evictLeastRecentlyUsed();
}
cacheEntries.put(entry.getKey(), entry);
}
}
}
private void evictLeastRecentlyUsed() {
// We could maintain a heap to make this faster, if the iteration turns out to be a bottleneck.
// Note: ConcurrentHashMap makes this code thread safe, although it is possible that the same entry
// is evicted more than once from different threads.
CacheEntry entryToEvict = null;
for (final CacheEntry entry : cacheEntries.values()) {
if (entryToEvict == null || entry.getAge() < entryToEvict.getAge()) {
entryToEvict = entry;
}
}
if (entryToEvict != null && cacheEntries.remove(entryToEvict.getKey()) != null) {
if (log.isTraceEnabled()) {
log.trace("Evicting from cache: " + entryToEvict.getKey());
}
entryToEvict.invalidate(); // potentially slow
}
}
public void entryClosed(final CacheEntry entry) {
cacheEntries.remove(entry.getKey());
}
public int size() {
return cacheEntries.size();
}
public long nextAge() {
// If the age counter ever reaches Long.MAX_VALUE, strange eviction behavior will follow.
// But it won't. I've done the math. :-)
return ageCounter.incrementAndGet();
}
}