java - 考虑到旧 key 的 Guava 缓存

标签 java caching guava google-guava-cache

我遇到了 Guava Caches 的问题。当我在缓存中只有一个元素时,一切都很好。但是当我加载第二个元素时,它试图用较早输入的键进行选择

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

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

我哪里错了?

最佳答案

您的问题与您如何创建 CacheLoader 有关,如果你检查得很好,你会看到你用给定的缓存键(缓存延迟初始化时局部变量 cacheKey 的值)初始化它,而它应该更通用并依赖于 key作为参数提供给方法 load你的CacheLoader否则它将通过调用 getValue(key) 加载缓存使用相同的 key 。

应该是这样的:

new CacheLoader<String, MyClass>() {
    @Override
    public MyClass load(String key) {
        return getValue(key); // instead of return getValue(cacheKey);
    }
}

注意:您初始化缓存的方式不是线程安全的,事实上如果它还没有被初始化并且您的方法method被多个线程同时调用,它将被创建多次而不是一次。

一种方法可能是使用双重检查锁定习惯用法,如下所示:

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()...

不需要懒惰地初始化它,这也会大大简化你的方法的代码,因为它会简单地减少到:

public MyClass method(final String id1, final long id2)  {
    log.error("inside with "+id1);
    final String cacheKey = id1+"-"+id2;
    try {
        return cache.get(cacheKey);
    } catch (ExecutionException ex) {
        log.error("EEE missing entry",ex);
    }
}

关于java - 考虑到旧 key 的 Guava 缓存,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39304919/

相关文章:

java - JDBI 可选注册

java - 在不覆盖 hashCode() 的情况下删除重复项

java - Spring + JQuery 动态绑定(bind)

Java 参数无法识别

java - 在java中搜索文本文件中的字符串时出现问题

http - 关于过期 header 的问题

java - 如何在 spring java 配置中设置日志记录属性?

java - Ignite C++ 和缓存亲和性

git - 忽略已经提交到 Git 存储库的文件

caching - Guava Cache 的 BiMap 功能?