番石榴缓存refreshAfterWrite混淆

pla*_*147 10 java caching future guava

我在使用Guava缓存时得到的结果并不是我真正理解的.

我正在实现一个我想要异步刷新的单个缓存.

我每秒都会点击缓存,然后将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();
    }

}
Run Code Online (Sandbox Code Playgroud)

Lou*_*man 8

你已经发现了一个合法的错误.(我帮忙维护common.cache.)

如果我正确地遵循了事情,我相信事件链如下:

让我们说得到A是第一个get导致刷新的东西,得到B是第一个get之后.

  • 获取A调用scheduleRefresh,refresh在执行程序中启动任务.条目值引用将替换为a LoadingValueReference,并loadAsync添加一个等待重新加载的侦听器来完成.
  • Get A重新加载的分叉任务完成并获取锁定.
  • 接听B电话scheduleRefresh.访问时间尚未更新,因此它继续进行,然后进入insertLoadingValueReference.
  • Get A重新加载的分叉任务更新写入时间,并使用a替换值引用StrongValueReference,因为加载已完成.锁被释放.
  • 获取B确定该值仍未处于加载过程中,因此它继续启动新的重新加载.

(更新:已提交https://code.google.com/p/guava-libraries/issues/detail?id=1211.)