我正在使用谷歌的番石榴库来创建我的缓存。我将使用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);
}
}
据我所知,如果我为没有缓存列表的域调用getBlockedURLList,它将首先加载列表,然后返回结果。但是,这不是我想要的行为。
如果密钥不在缓存中,我希望缓存调用它的加载函数,但我希望异步调用。所以,如果密钥不存在,我会说这次我可以在缓存中没有密钥的情况下工作,然后继续前进,但我希望下次尝试获取密钥时密钥存在。本质上,我想以非阻塞的方式调用load()。也就是说,cache.get()不应该等到首先从load()获取结果。
这是缓存解释中的示例
// 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;
}
}
});
在您的情况下,您可以选择从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,每个键只能启动一个后台任务。