簡體   English   中英

番石榴緩存refreshAfterWrite混亂

[英]Guava caching refreshAfterWrite confusion

使用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();
    }

}

認為您已經找到了合法錯誤。 (我幫助維護common.cache 。)

如果我正確地遵循事情,我相信一系列事件如下:

假設get A是導致刷新的第一個get ,而get B是之后的第一個get

  • Get A調用scheduleRefresh ,這將在執行scheduleRefresh中啟動refresh任務。 將條目值引用替換為LoadingValueReference ,並且loadAsync添加一個偵聽器,等待重新加載完成。
  • Get A重新加載的分叉任務完成並獲取鎖。
  • 獲取B調用scheduleRefresh 訪問時間尚未更新,因此將繼續進行,並進入insertLoadingValueReference
  • 由於加載已完成,因此Get A重新加載的分叉任務將更新寫入時間,並用StrongValueReference替換值引用。 鎖被釋放。
  • Get B確定該值仍不在加載過程中,因此它將繼續啟動新的重新加載。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM