4

我有一个用例,其中加载缓存数据的方法是批量调用,但我永远不会使用 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;
    }
}
4

1 回答 1

10

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

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);
  }
}
于 2013-08-16T11:06:27.850 回答