番石榴缓存考虑旧密钥

时间:2016-09-03 08:35:14

标签: java caching guava google-guava-cache

我正面临着番石榴缓存的问题。当我在缓存中只有一个元素时,事情就好了。但是当我加载第二个元素时,它试图用早期条目的键选择

private static LoadingCache<String, MyClass> cache = null;
....
public MyClass method(final String id1, final long id2)  {
    log.error("inside with "+id1);
    final String cacheKey = id1+"-"+id2;
    if(cache == null){
        cache = CacheBuilder.newBuilder()
       .maximumSize(1000)
       .build(
            new CacheLoader<String, MyClass>() {
                @Override
                public MyClass load(String key) {
                    return getValue(cacheKey);
                }
           }
        );
    }
    try {
        return cache.get(cacheKey);
    } catch (ExecutionException ex) {
        log.error("EEE missing entry",ex);
    }
}

private MyClass getValue(String cacheKey){
    log.error("not from cache "+cacheKey);
    ...

}

日志说:

inside with 129890038707408035563943963861595603358
not from cache 1663659699-315839912047403113610285801857400882820 // This is key for the earlier entry

例如,当我调用方法(“1”,2)时,它会在缓存中加载值,然后我可以从缓存中获取它。现在我调用方法(“3”,4),这不在缓存中,因此调用getValue()并且日志打印出方法的键(“1”,2)

我哪里错了?

1 个答案:

答案 0 :(得分:3)

您的问题与您创建CacheLoader的方式有关,如果检查得好,您会看到使用给定的缓存键(当前变量cacheKey的值)初始化它缓存是懒惰的初始化)虽然它应该更通用,并依赖key作为load的方法CacheLoader的参数提供,否则它将通过调用{{1}加载缓存使用相同的密钥。

应该是这样的:

getValue(key)

NB:初始化缓存的方式是非线程安全,实际上如果它尚未初始化并且您的方法new CacheLoader<String, MyClass>() { @Override public MyClass load(String key) { return getValue(key); // instead of return getValue(cacheKey); } } 被多个线程调用同时它将被创建几次而不是一次。

一种方法是使用双重检查的锁定习语作为下一个:

method

NB:不要使用基于非静态方法的private static volatile LoadingCache<String, MyClass> cache = null; public MyClass method(final String id1, final long id2) { ... if(cache == null){ synchronized (MyClass.class) { if(cache == null){ cache = ... } } } 初始化静态缓存,它也是容易出错。让它们非静态静态,但不要混合它们。

假设你可以同时进行静态,你的缓存初始化将非常简单,它只是:

CacheLoader

无需懒惰地初始化它,这也将简化您方法的代码,因为它只会简化为:

private static final LoadingCache<String, MyClass> cache = CacheBuilder.newBuilder()...