Guava CacheBuilder Cache RemovalListener onRemoval每次都会被调用,但条目被删除

时间:2012-08-28 10:06:45

标签: java guava

如果没有写入密钥,我已经创建了一个基于Guava CacheBuilder的缓存,有效期为5秒。已向其添加了removeListener,用于打印要删除的键/值对。 我观察到的是,只有第一次调用侦听器的onRemoval方法。第二次删除条目时不会调用它。 (实际删除发生。只是removeListener的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: {}

3 个答案:

答案 0 :(得分:8)

删除实际上并不是直接发生的:Guava没有自己的清理线程来删除过期的条目。删除通常发生在高速缓存的同一段中写入时,或者在经过一定时间后读取时(为了分摊删除成本)。但是,虽然该条目仍然存在,但它被视为过期,这就是为什么它没有打印。

引用CacheBuilder's javadoc

  

如果请求expireAfterWrite或expireAfterAccess,则可能会在每次缓存修改,偶尔的缓存访问或对Cache.cleanUp()的调用时逐出条目。过期的条目可以在Cache.size()中计算,但对于读取或写入操作永远不可见。

答案 1 :(得分:1)

作为Guava Cache的替代方案,您可以使用Caffeine&#34;基于Java 8&#34; 的高性能,近乎优化的缓存库。< / p>

它有&#34; Google Guava启发的API&#34; 并定期执行驱逐,因此它可能更适合您的用例(如果您担心内存未被释放半 - 自动)。见Eviction wiki page

  

在写入期间以及在读取期间偶尔进行定期维护。调度和触发到期事件在摊销的O(1)时间内执行。

答案 2 :(得分:0)

  

如果请求expireAfterWriteexpireAfterAccess,则可能会在每次缓存修改,偶尔缓存访问或调用Cache.cleanUp()时逐出条目。过期的条目可能会计入Cache.size(),但读取或写入操作永远不会显示。

从长远来看,这将最终占用所有堆内存或导致其他未使用任何缓存的模块的内存不足。