1

我使用番石榴缓存支持为测试缓存过期编写了以下代码。在下面的代码中,我创建了一个缓存,从键 11000 到 30000 添加 20 个条目,经过一些休眠遍历缓存中存在键并搜索两个键(19000 和 29000)

import com.google.common.cache.*;
import java.util.concurrent.TimeUnit;

public class TestGuavaCache {

  public static int evictCount = 0;

  public static void main(String[] args) throws InterruptedException {

    Cache<Integer, Record> myCache = CacheBuilder.newBuilder()
            .expireAfterAccess(10, TimeUnit.SECONDS)
            .expireAfterWrite(15, TimeUnit.SECONDS)
            .concurrencyLevel(4)
            .maximumSize(100)
            .removalListener(new RemovalListener<Object, Object>() {
                @Override
                public void onRemoval(RemovalNotification<Object, Object> notification) {
                    evictCount++;
                    System.out.println(evictCount + "th removed key >> " + notification.getKey()
                            + " with cause " + notification.getCause());
                }
            })
            .recordStats()
            .build();

    int nextKey = 10000;

    for (int i = 0; i < 20; i++) {

        nextKey = nextKey + 1000;

        myCache.put(nextKey, new Record(nextKey, i + " >> " + nextKey));

        Thread.sleep(1000);
    }

    System.out.println("=============================");
    System.out.println("now go to sleep for 20 second");

    Thread.sleep(20000);

    System.out.println("myCache.size() = " + myCache.size());

    for (Integer key : myCache.asMap().keySet()) {
        System.out.println("next exist key in cache is" + key);
    }
    System.out.println("search for key " + 19000 + " : " + myCache.getIfPresent(19000));
    System.out.println("search for key " + 29000 + " : " + myCache.getIfPresent(29000));
}
}

class Record {

  int key;
  String value;

  Record(int key, String value) {
    this.key = key;
    this.value = value;
 }

}

运行以上主要方法后,我看到以下结果

1th removed key >> 11000 with cause EXPIRED
2th removed key >> 13000 with cause EXPIRED
3th removed key >> 12000 with cause EXPIRED
4th removed key >> 15000 with cause EXPIRED
5th removed key >> 14000 with cause EXPIRED
6th removed key >> 16000 with cause EXPIRED
7th removed key >> 18000 with cause EXPIRED
8th removed key >> 20000 with cause EXPIRED
=============================
now go to sleep for 20 second
myCache.size() = 12
search for key 19000 : null
search for key 29000 : null

我有 3 个问题

  1. 为什么其他类似 17000,19000,25000 的密钥没有在 RemovalListener 中通知
  2. 为什么缓存键集上的迭代为空,而缓存大小为 12
  3. 为什么在缓存大小为 12 时搜索 19000 和 29000 为空
4

2 回答 2

3

直接来自 Javadocs:

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

Guava 的缓存不会在过期时间过后立即清理条目;这是因为它(故意)不创建一个额外的线程来维护缓存。经常对各种查询操作执行清理。特别是,上述文档解释了该size()方法可能会暂时计算过期条目。

于 2013-09-30T16:37:38.993 回答
0

此外,对于花费数小时试图找出第一个问题的答案的任何人(如我):

创建并发级别大于 1 的 guava 缓存会在内部创建固定数量的哈希表段,并将条目分配给任何已创建的哈希表段。当缓存访问/写入发生在条目所在的哈希表段上时,RemovalListener 只会收到这些条目的通知。

于 2017-08-21T19:05:28.220 回答