番石榴缓存 - 如何在任何错过时加载AllAll?

时间:2013-04-15 19:45:33

标签: java caching guava

我有一个用例,其中加载我的缓存数据的方法是批量调用,但我永远不会使用getAll从缓存中获取数据。有没有办法在一个loadAll上有多个并发获取所有块?我不希望个人使用不同的密钥导致多次调用数据源。

cache.get(key1); // missing entry, starts refresh
cache.get(key2); // separate thread, already getting reloaded due to key1 miss

我认为在查看LocalCache之后我必须实现自己的同步,在我的数据访问器中使用类似本地缓存的东西,只允许通过每隔这么多单位时间进行调用。当呼叫确实通过时,使用单个赋值语句更新本地副本。

我是否遗漏了Guava缓存库中的内容?

修改

我正在考虑以下内容。但是,在loadAll完成时,它可能会继续返回过时的数据。我更喜欢load处的所有阻止,只有第一个请求会导致loadAll继续。

public class DataCacheLoader extends CacheLoader<String, Double>
{
    private final Cache<String, Double> cache;
    private ConcurrentMap<String, Double> currentData;
    private final AtomicBoolean isloading;

    public DataCacheLoader( final Cache<String, Double> cache )
    {
        this.cache = cache;
        isLoading = new AtomicBoolean( false );
    }

    @Override
    public Double load( final String key ) throws Exception
    {
        if ( isLoading.compareAndSet( false, true ) )
        {
            cache.putAll( loadAll( Lists.newArrayList( key ) ) ) );
        }
        return currentData.get( key );
    }

    @Override
    public Map<String, Double> loadAll(Iterable<? extends String> keys) throws Exception
    {
        currentData = source.getAllData();
        return currentData;
    }
}

1 个答案:

答案 0 :(得分:8)

这是一个应该解决问题的解决方案。我们的想法是,不是缓存每个单独的密钥,而是使用一个固定密钥缓存整个地图。一个缺点是您将无法使底层地图的各个部分到期(至少不容易),但这可能不是必需的。

class MyCache {
  private static final Object KEY = new Object();
  private final LoadingCache<Object, Map<String, Double>> delegate = 
      new CacheBuilder()
      // configure cache
          .build(new CacheLoader<Object, Map<String, Double>>() {
             public Map<String, Double> load(Object key) {
               return source.load();
             }
          };
  double get(String key) {
    return cache.get(KEY).get(key);
  }
}