我正在使用Guava的LoadingCache进入我的项目来处理线程 - {安全,友好}缓存加载,它运行得非常好。但是,有一个限制。
定义缓存的当前代码如下所示:
cache = CacheBuilder.newBuilder().maximumSize(100L).build(new CacheLoader<K, V>()
{
// load() method implemented here
}
我没有指定到期时间。
问题在于,根据密钥的值,某些关联值可能会过期而其他值可能不会过期。并且CacheLoader
没有考虑到这一点,如果您指定到期时间,则它适用于每个条目。
你会如何解决这个问题?
答案 0 :(得分:27)
另一个替代方案是ExpiringMap,它支持变量输入到期:
Map<String, String> map = ExpiringMap.builder().variableExpiration().build();
map.put("foo", "bar", ExpirationPolicy.ACCESSED, 5, TimeUnit.MINUTES);
map.put("baz", "pez", ExpirationPolicy.CREATED, 10, TimeUnit.MINUTES);
答案 1 :(得分:11)
我建议您将过期时间直接包含在您的入门类中,如果从缓存中获取后立即过期,则从缓存中手动逐出它:
MyItem item = cache.getIfPresent(key);
if (item != null && item.isExpired()) {
cache.invalidate(key);
item = cache.get(key);
// or use cache.put if you load it externally
}
作为替代方案,我建议您检查支持每个元素到期策略的EhCache库。
答案 2 :(得分:6)
LoadingCache
提供了一些常用的过期策略,但是如果这些政策不能满足您的需求,则需要自行推广。
只需添加DelayQueue即可。每当您向缓存添加内容时,请向该队列添加Delayed
,并使用适当的到期时间。 Delayed
对象应该具有对该键的(弱?)引用。
最后一个要素是你需要定期轮询这个队列,看看是否有东西过期而且必须被驱逐。不必添加线程来执行此操作,您可以捎带访问LoadingCache
的任何线程。就在访问缓存之前,例如:
private void drainCache() {
MyDelayed expired;
while ((expired = delayedQueue.poll()) != null) {
K key = expired.getReference();
if (key != null) { // this only in case if you hold the key in a weak reference
loadingCache.invalidate(key);
}
}
}
..
V lookup(K key) {
drainCache();
return loadingCache.getUnchecked(key);
}
答案 3 :(得分:0)
我猜你可以使用显式的invalidate来确切地定义哪些条目应该被驱逐,但这可能不是你想要的。
您可以为条目赋予不同的权重。它并不完美,但您可以指导缓存以逐出不太重要的条目。参见Weighter,权重为0的条目不会被基于大小的驱逐驱逐。
答案 4 :(得分:0)
如果条目很大并且您需要节省内存,我认为没有一个很好的解决方案,但这些黑客的想法是:
使用按到期时间排序的PriorityQueue
手动删除条目。如果你想确保没有过期的条目被使用,你需要将它与hoaz的解决方案结合起来;队列只能防止无用的条目占用内存。
您写道“某些关联值可能会过期而其他值可能不会过期”,这表明所有过期条目的过期延迟都是相同的。这样可以使用更简单,更快捷Queue
(例如ArrayDeque
代替PriorityQueue
)。
如果到期延迟相当大,您可以让所有条目过期,并重新插入那些永远存在于RemovalListener
中的条目。这可能会以两种方式失败:1。在此期间您可能会错过。 2.删除和重新插入可能会耗费大量的CPU时间。
答案 5 :(得分:0)
您可以使用受番石榴启发的咖啡因库。 这是来自github repo
的示例用法LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.expireAfterAccess(5, TimeUnit.MINUTES)
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
.expireAfterCreate(5, TimeUnit.MINUTES)