5

我有一个像这样的加载缓存:

MyCacheLoader loader=new MyCacheLoader();
MyRemovalListener listener=new MyRemovalListener();
LoadingCache<String, String> myCache = CacheBuilder.newBuilder()
                .concurrencyLevel(10)
                .weakKeys()
                .maximumSize(2)
                .expireAfterWrite(120, TimeUnit.SECONDS)
                .removalListener(listener)
                .build(loader);

但是当我做这样的事情来测试时:

System.out.println(myCache.get("100"));
System.out.println(myCache.get("103"));
System.out.println(myCache.get("110"));
System.out.println(myCache).get("111"));
Thread.sleep(230000);
System.out.println(myCache.size());

我仍然得到 2 而不是零。为什么 ?超过 120 秒后,如果没有错,我应该得到零尺寸?

MyCacheLoader

public class MyCacheLoader extends CacheLoader<String,String>     {
    @Override
    public String load(String key) throws Exception {
        return key;
    }
}

MyRemovalListener

public class MyRemovalListener implements RemovalListener<String,String> {

    Logger logger = LoggerFactory.getLogger(MyRemovalListener.class);
    @Override
    public void onRemoval(RemovalNotification<String, String> removalNotification) {
        logger.info("Message with message Id ("+
                removalNotification.getKey()+ ") is removed.");
        System.out.println("Message with message Id ("+
                removalNotification.getKey()+ ") is removed.");
    }
}
4

1 回答 1

8

首先,如果您不熟悉 Guava 的 Cache API,或者如果您将其视为实现了Map<K, V>接口,那么我建议您阅读CachesExplained · google/guava Wiki。“ACache与 相似ConcurrentMap,但不完全相同。”

例如Cache.size()“返回此缓存中的近似条目数”(添加了重点)与Map.size()“返回此映射中的键值映射的数量”不同。

因此,我们不应该期望Cache.size()返回确切数量的条目,而是一个近似值Cache.size()尽管我肯定会在Map.size().

其次,“构建的缓存不会CacheBuilder‘自动’或在值过期后立即执行清理和驱逐值,或任何类似的操作。相反,它会在写操作期间或偶尔的读操作期间执行少量维护,如果写是稀有”(何时进行清理?· CachesExplained· google/guava Wiki)。

考虑以下示例:

LoadingCache<String, String> myCache = CacheBuilder.newBuilder()
        .concurrencyLevel(10)
        .weakKeys()
        .maximumSize(2)
        .expireAfterWrite(120, TimeUnit.MILLISECONDS)
        .removalListener(notification ->
                System.out.println(notification.getKey() + " removed"))
        .build(new CacheLoader<String, String>() {
            @Override
            public String load(String key) throws Exception {
                System.out.println("loading " + key);
                return UUID.randomUUID().toString();
            }
        });
myCache.get("100");
myCache.get("103");
myCache.get("110");
myCache.get("111");
Thread.sleep(230);
System.out.println(myCache.size());
myCache.get("111");
System.out.println(myCache.size());

输出:

loading 100
loading 103
loading 110
100 removed
loading 111
103 removed
2
110 removed
111 removed
loading 111
1

如您所见,在从缓存中读取项目之前,这里不会发生驱逐。

有关更多详细信息以及更详细的示例,请参阅我对定时缓存到期如何工作的回答

于 2016-09-08T13:35:44.447 回答