2012-08-28 36 views
3

如果沒有寫入密鑰,我已經創建了一個基於Guava CacheBuilder的緩存,其失效時間爲5秒。已經添加了一個removeListener來打印正在被刪除的鍵/值對。 我觀察到的是監聽器的onRemoval方法只在第一次被調用。第二次刪除條目時不會調用它。 (實際的刪除發生,只是removeListener的onRemoval方法沒有被調用)。Guava CacheBuilder緩存RemovalListener onRemoval每次不會被調用,但條目被刪除

我做錯了什麼?有人可以幫忙嗎?提前致謝。 這裏是我的代碼:

import java.util.Map; 
import java.util.concurrent.ConcurrentHashMap; 
import java.util.concurrent.TimeUnit; 

import com.google.common.cache.Cache; 
import com.google.common.cache.CacheBuilder; 
import com.google.common.cache.RemovalListener; 
import com.google.common.cache.RemovalNotification; 


public class TestCacheBuilder { 

    public static void main(String[] args) { 
    try { 
     new TestCacheBuilder(); 
    }catch (Exception e){  
    e.printStackTrace(); 
    } 
    } 

    public TestCacheBuilder() { 

    Cache<String, String> myCache = CacheBuilder.newBuilder() 
     .expireAfterWrite(5, TimeUnit.SECONDS) 
     .removalListener(new RemovalListener<String, String>() { 
      public void onRemoval(RemovalNotification<String, String> removal) { 
      System.out.println("removal: "+removal.getKey()+"/"+removal.getValue()); 
      }   
     }) 
     .build(); 


    Map<String, String> inMap = myCache.asMap(); 

    inMap.put("MyKey", "FirstValue"); 

    System.out.println("Initial Insert: "+inMap); 

    //Wait 16 seconds 

    try { 
     Thread.sleep(4000); 
    } catch(InterruptedException ex) { 
     Thread.currentThread().interrupt(); 
    } 

    System.out.println("After 4 seconds: " + inMap); 

    inMap.put("MyKey", "SecondValue"); 

    try { 
     Thread.sleep(1000); 
    } catch(InterruptedException ex) { 
     Thread.currentThread().interrupt(); 
    } 

    System.out.println("After 1 more second: " + inMap); 

    try { 
     Thread.sleep(4000); 
    } catch(InterruptedException ex) { 
     Thread.currentThread().interrupt(); 
    } 


    System.out.println("After 4 more seconds: " + inMap); 

    } 

} 

的輸出如下:

Initial Insert: {MyKey=FirstValue} 
After 4 seconds: {MyKey=FirstValue} 
removal: MyKey/FirstValue 
After 1 more second: {MyKey=SecondValue} 
After 4 more seconds: {} 
+0

另請參閱http://stackoverflow.com/questions/10626720/guava-cachebuilder-removal-listener –

+1

除了實際的問題:您可以使用[Ticker](http://docs.guava)來增強您的測試-libraries.googlecode.com/git-history/release/javadoc/com/google/common/base/Ticker.html)模擬一個時鐘,您可以根據自己的意願操作([請參閱此處](http:// code。 google.com/p/guava-libraries/wiki/CachesExplained#Eviction))。這將加速您的測試,並使其適應微妙的時序問題。 – Pyranja

+0

謝謝Pyranja。肯定會檢查出來! – Kiran

回答

8

去除實際上並不直接發生:番石榴沒有自己的清潔線程刪除過期條目。刪除通常發生在緩存的同一段中,或者在讀取過程中經過了一段時間(以分攤刪除成本)時寫入。但是,雖然條目仍然存在,但它被視爲已過期,這就是爲什麼它沒有打印。

引用CacheBuilder's javadoc

如果expireAfterWrite或expireAfterAccess被請求的條目可以在每個高速緩存的修改被逐出,上偶爾緩存訪問,或對調用Cache.cleanUp()。過期的條目可能會在Cache.size()中計數,但對於讀取或寫入操作永遠不可見。

+0

謝謝弗蘭克。 Cache.cleanUp()似乎有訣竅。只是想知道如果經常使用它會有任何性能影響。我面臨的問題是當緩存條目被刪除時,我需要消息,因爲系統需要根據刪除操作做其他事情。 – Kiran

+0

@Kiran它取決於緩存的訪問模式。如果它被稱爲很多,那麼在運行'Cache.cleanUp()'時會添加一些爭用。 –

+1

@Kiran:你的緩存越忙,自動清理就越多,所以在高峯期你可能會關閉手動清理程序。 OTOH,我相信已經看到一次調用'cleanUp'並沒有消除所有的垃圾。如果這是真的,也許你應該諮詢番石榴傢伙。 – maaartinus

0

如果expireAfterWriteexpireAfterAccess被請求時,條目可以在每個高速緩存的修改被逐出,上偶爾緩存訪問,或對調用Cache.cleanUp()。過期的條目可能會計入Cache.size(),但在讀取或寫入操作時永遠不可見。

從長遠來看,這將最終導致吃掉所有堆內存或導致其他未使用任何緩存的模塊導致內存不足。