我在使用Guava Caches时得到的结果并不是我真正理解的。
我正在实现一个我想要异步刷新的单个缓存。
我每秒都会点击缓存,并且我将refreshAfterWrite设置为20秒。 我的加载/重载功能需要5秒钟。
如果我在加载/重新加载方法的开头打印出当前时间 - 我会期待这样的结果:
加载呼叫从00:00:00开始 重新加载呼叫在00:00:25开始 重新加载呼叫从00:00:50开始
因此负载将花费5秒钟,然后下一次写入将在此后20秒触发(5 + 20 = 25)。该写入将在50秒后(25 + 5 + 20 = 50)秒发生..等等
相反,我得到:
加载呼叫从00:00:00开始 重新加载呼叫在00:00:25开始 重新加载呼叫从00:00:30开始
这表明第二次重新加载是在第一次重新加载完成后直接发生的。
我认为写入会在未来处理后发生,所以下次重新加载会在此之后安排20秒?
我是否发现了一个错误,或者我对refreshAfterWrite的工作原理有一个基本的误解?
示例代码如下:
private static SimpleDateFormat format = new SimpleDateFormat("hh:mm:ss");
public static void main(String[] args) throws ExecutionException, InterruptedException {
final ExecutorService executor = Executors.newFixedThreadPool(3);
final LoadingCache<String, Long> cache = CacheBuilder.newBuilder().maximumSize(1) //
.refreshAfterWrite(20, TimeUnit.SECONDS)//
.build(new CacheLoader<String, Long>() {//
public Long load(String key) {
return getLongRunningProcess("load", key);
}
public ListenableFuture<Long> reload(final String key, Long prevGraph) {
ListenableFutureTask<Long> task = ListenableFutureTask.create(new Callable<Long>() {
public Long call() {
return getLongRunningProcess("reload", key);
}
});
executor.execute(task);
return task;
}
});
while (true) {
Thread.sleep(1000L);
cache.get(CACHE_KEY);
}
}
private static Long getLongRunningProcess(String callType, String key) {
System.out.printf("%s call started at %s\n", callType, format.format(new Date()));
try {
Thread.sleep(5000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
return counter.getAndIncrement();
}
}
答案 0 :(得分:8)
我认为你发现了一个合法的错误。 (我帮助维护common.cache
。)
如果我正确地遵循了事情,我相信事件链如下:
假设get A是导致刷新的第一个get
,而B之后是第一个get
。
scheduleRefresh
,启动执行程序中的refresh
任务。条目值引用将替换为LoadingValueReference
,loadAsync
会添加一个等待重新加载的侦听器。scheduleRefresh
。访问时间尚未更新,因此会继续,并进入insertLoadingValueReference
。StrongValueReference
替换值引用,因为加载已完成。锁被释放。(更新:提交https://code.google.com/p/guava-libraries/issues/detail?id=1211。)