2012-11-22 49 views
10

我得到的結果,我真的不明白,當使用番石榴緩存。番石榴緩存refreshAfterWrite混淆

我正在實現一個我想異步刷新的密鑰緩存。

我每秒都會打開緩存,並且我已經將refreshAfterWrite設置爲20秒。 我的加載/重新加載功能需要5秒鐘。

如果我打印出在負載/重裝方法開始時的當前時間 - 我希望一些結果是這樣的:

負載通話開始於00:00:00
重裝通話開始於0點00分25秒
重載呼叫開始在零時00分五十秒

所以負載將需要5秒之後(5 + 20 = 25)的下一個寫入將觸發20秒。這寫將發生在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

幾個星期前,我一直在尋找一個遺忘的單個元素的緩存功能。可悲的是,番石榴不支持這一點,所以我自己寫了一個。看看我在http://codereview.stackexchange.com/questions/18056/ability-to-forget-the-memoized-supplier-value上這樣做的嘗試,如果需要的話可以隨意使用此代碼。 – mindas

回答

7

認爲你發現了一個合法的錯誤。 (我幫助維持common.cache

如果我正確地下面的事情,我相信事件鏈如下:

比方說,讓A是第一get引起刷新,並獲得B是之後的第一個get

  • 撥打電話scheduleRefresh,在執行程序中啓動refresh任務。輸入值參考替換爲LoadingValueReference,並且loadAsync添加一個偵聽器,等待重新加載完成。
  • Get A重載的分叉任務完成並獲取鎖。
  • 撥打B電話scheduleRefresh。訪問時間尚未更新,因此它繼續進行,並進入insertLoadingValueReference
  • 獲取A重載的分叉任務更新寫入時間,並且由於加載完成而將值引用替換爲StrongValueReference。鎖被釋放。
  • Get B確定該值不在加載過程中,因此它繼續啓動新的重新加載。

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

+0

感謝您的回覆!我的基本理解是關於正確的行爲應該是什麼?也就是說,如果緩存以小於執行刷新所需的時間的規律間隔接收到請求,則我們可以假定刷新將以固定的時間間隔開始,該時間間隔對應於:(執行刷新所花費的時間)+(刷新間隔持續時間)? - 不是我會基於此解決方案,因爲調度定期刷新會更清潔! – plasma147

+0

這與我對規範的閱讀是一致的,是的。 –