9

在 guava 中,使用 LoadingCache 时会同步调用 CacheLoader。但是,我的load()操作可能需要太长时间(~1 秒),我想采取默认操作以防它需要太长时间(> 200 毫秒)并异步加载值。

有没有办法做到这一点?或者您还有其他方法可以推荐吗?

4

3 回答 3

10

The Caffeine library is a Java 8 rewrite of Guava's cache that allows asynchronous automatic loading of entries into a cache, returning CompletableFutures. It is written by people who were directly involved in creating the Guava cache, and uses a Guava-inspired API (including an adapter to Guava's interfaces).

According to posts on the Guava mailing list, it is based on the original proposal for the Guava caching library, and includes changes that were originally intended for Guava itself, but were not included for various reasons (including Guava's need to be compatible with older versions of Java).

In fact, some projects now consider the Guava cache deprecated and use Caffeine instead, e.g. Spring has switched to Caffeine and the author states that "Caffeine is the Java 8 successor to ConcurrentLinkedHashMap and Guava's cache. Projects should prefer Caffeine and migrate when requiring JDK8 or higher."

于 2018-09-15T13:35:14.437 回答
5

您可以按正常方式执行此操作:提交一个任务以将缓存值发送到 an ExecutorService,调用get(200, MILLISECONDS)theFuture并在超时时执行其他任何操作。

例子:

final LoadingCache<Key, Result> cache = ...
final Key key = ...
ExecutorService executor = ...

Future<Result> future = executor.submit(new Callable<Result>() {
  @Override public Result call() throws Exception {
    return cache.get(key);
  }
});

try {
  Result result = future.get(200, TimeUnit.MILLISECONDS);
  // got the result; do stuff
} catch (TimeoutException timeout) {
  // timed out; take default action
}
于 2013-07-04T20:52:41.883 回答
1

您可以使用 getIfPresent 命令检查该值是否为空。如果值为空,那么您可以提交一个异步加载值的任务并继续您的流程。

例子 :

Map<String, String> map = cache.getIfPresent(key);
if(map == null){
 executorService.submit(new Callable<Map<String, String>>() {
    @Override
    public Map<String, String> call() throws Exception {
        return cache.get(key);
    }   
 }); 
}
else{
    continue with the flow...
}

如果您想在仍在读取旧值的同时刷新缓存中的值,您还应该使用 refreshAfterWrite 功能并实现 reload 方法。通过这种方式,缓存将始终更新,并且读取值的主线程不会受到影响。

例子 :

cache = CacheBuilder.newBuilder()
    .refreshAfterWrite(30, TimeUnit.SECONDS)
    .build( 
        new CacheLoader<String, Map<String,String>>() {

            public Map<String, String> load(String key) throws Exception {
                map = hardWork(key)//get map from from DB -- expensive time commend
                return map;
            }

            @Override
                public ListenableFuture<Map<String, String>> reload(final String key, Map<String, String> oldValue) throws Exception {
                    // we need to load new values asynchronously, so that calls to read values from the cache don't block
                    ListenableFuture<Map<String, String>> listenableFuture = executorService.submit(new Callable<Map<String, String>>() {

                        @Override
                        public Map<String, String> call() throws Exception {
                            //Async reload event
                            return load(key);
                        }
                    }); 

                    return listenableFuture;
                }
    });
于 2016-07-30T07:07:26.487 回答