我正在使用Google的guava库来创建缓存。我将使用LoadingCache接口。以下是代码段
public class BlockedURLs {
private static final long MAX_SIZE = 1000;
private static final long CACHE_DURATION = 2;
private static LoadingCache<String, ArrayList<String> > lruBlockedURLCache;
BlockedURLs() {
lruBlockedURLCache = CacheBuilder
.newBuilder()
.maximumSize(MAX_SIZE)
.expireAfterWrite(CACHE_DURATION, TimeUnit.HOURS)
.build(new CacheLoader<String, ArrayList<String>>() {
@Override
public ArrayList<String> load(String s) throws Exception {
return BlockedURLLoader.fetchBlockedURLListFromRedis(s);
}
});
}
public static ArrayList<String> getBlockedURLList(String domain) {
return lruBlockedURLCache.getUnchecked(domain);
}
}
据我了解,如果我为没有缓存List的域调用getBlockedURLList,它将首先加载列表然后返回结果。但是,这不是我想要的行为。
如果密钥不在缓存中,我希望缓存调用它的加载函数,但我想异步执行。因此,如果密钥不存在,我会说我这次可以使用缓存中的密钥并继续工作,但我希望下次尝试获取密钥时存在密钥。本质上,我想以非阻塞方式调用load()。也就是说,cache.get()不应该等到首先从load()获取结果。
答案 0 :(得分:1)
// Some keys don't need refreshing, and we want refreshes to be done asynchronously.
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
.maximumSize(1000)
.refreshAfterWrite(1, TimeUnit.MINUTES)
.build(new CacheLoader<Key, Graph>() {
public Graph load(Key key) { // no checked exception
return getGraphFromDatabase(key);
}
public ListenableFuture<Graph> reload(final Key key, Graph prevGraph) {
if (neverNeedsRefresh(key)) {
return Futures.immediateFuture(prevGraph);
} else {
// asynchronous!
ListenableFutureTask<Graph> task = ListenableFutureTask.create(new Callable<Graph>() {
public Graph call() {
return getGraphFromDatabase(key);
}
});
executor.execute(task);
return task;
}
}
});
答案 1 :(得分:1)
在您的情况下,您可以选择从load()返回指示缺席的信号值,然后在后台填充缓存。
public Optional<List<String>> load(final Key key) {
exec.submit(some-task-that-calls Cache.put(K,V));
return Optional.absent();
}
LoadingCache<String,Optional<List<String>>> cache = ...
因此,在这种情况下,您可以通过立即提供信号值来绕过等待密钥可用的默认行为。检索到URL时,任务应调用Cache.put(K,V),使数据可用于后续调用。
您可以直接使用ConcurrentMap,但通过使用LoadingCache,您只需为每个键启动一个后台任务。