如何使LoadingCache以非阻塞方式调用load()?

时间:2013-12-10 13:56:04

标签: java caching guava nonblocking

我正在使用Google的guava库来创建缓存。我将使用LoadingCache接口。以下是代码段

public class BlockedURLs {
private static final long MAX_SIZE = 1000;
private static final long CACHE_DURATION = 2;

private static LoadingCache<String, ArrayList<String> > lruBlockedURLCache;

BlockedURLs() {
    lruBlockedURLCache = CacheBuilder
            .newBuilder()

            .maximumSize(MAX_SIZE)

            .expireAfterWrite(CACHE_DURATION, TimeUnit.HOURS)

            .build(new CacheLoader<String, ArrayList<String>>() {
                @Override
                public ArrayList<String> load(String s) throws Exception {
                    return BlockedURLLoader.fetchBlockedURLListFromRedis(s);
                }

            });
}

public static ArrayList<String> getBlockedURLList(String domain) {
    return lruBlockedURLCache.getUnchecked(domain);
}
}

据我了解,如果我为没有缓存List的域调用getBlockedURLList,它将首先加载列表然后返回结果。但是,这不是我想要的行为。

如果密钥不在缓存中,我希望缓存调用它的加载函数,但我想异步执行。因此,如果密钥不存在,我会说我这次可以使用缓存中的密钥并继续工作,但我希望下次尝试获取密钥时存在密钥。本质上,我想以非阻塞方式调用load()。也就是说,cache.get()不应该等到首先从load()获取结果。

2 个答案:

答案 0 :(得分:1)

这是Caches Explained

的示例
// Some keys don't need refreshing, and we want refreshes to be done asynchronously. 
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()        
   .maximumSize(1000)        
   .refreshAfterWrite(1, TimeUnit.MINUTES)        
   .build(new CacheLoader<Key, Graph>() {              
      public Graph load(Key key) { // no checked exception                
         return getGraphFromDatabase(key);              
      }       

      public ListenableFuture<Graph> reload(final Key key, Graph prevGraph) {        
        if (neverNeedsRefresh(key)) {                  
            return Futures.immediateFuture(prevGraph);                
        } else {
            // asynchronous!                  
            ListenableFutureTask<Graph> task = ListenableFutureTask.create(new Callable<Graph>() {                    
                public Graph call() {                      
                    return getGraphFromDatabase(key);                    
                }                  
            });                  
            executor.execute(task);                  
            return task;                
        }
          }
        });

答案 1 :(得分:1)

在您的情况下,您可以选择从load()返回指示缺席的信号值,然后在后台填充缓存。

public Optional<List<String>> load(final Key key) {
    exec.submit(some-task-that-calls Cache.put(K,V));
    return Optional.absent();
}
LoadingCache<String,Optional<List<String>>> cache = ...

因此,在这种情况下,您可以通过立即提供信号值来绕过等待密钥可用的默认行为。检索到URL时,任务应调用Cache.put(K,V),使数据可用于后续调用。

您可以直接使用ConcurrentMap,但通过使用LoadingCache,您只需为每个键启动一个后台任务。