Guava缓存器加载数据的方式有两种:同步(loadSync)与异步(loadAsync)。
同步方式——
只在取数据 V get(K, int, CacheLoader)执行过程中会被调用到。
同步加载数据代码如下:
ListenableFuture<V> loadingFuture = loadingValueReference.loadFuture(key, loader);
return getAndRecordStats(key, hash, loadingValueReference, loadingFuture);
异步方式——
只在刷新数据refresh(K, int, CacheLoader, boolean)执行过程中会被调用到。
guava缓存器没有提供类似refreshAll()的方法来刷新所有值,而只是提供了 refresh(Key)的方法来刷新某个键值对,这里的刷新动作是异步执行的,如果有人在值被彻底刷新之前,取这个Key的值,返回的还是刷新前的值。
在执行refresh时,会调用loadAsync方法来异步加载数据,同时通过isDone()方法来判断任务是否完成,正常结束、异常或者是被取消,这个方法都会返回true。
ListenableFuture<V> result = loadAsync(key, hash, loadingValueReference, loader);
if (result.isDone()) {
try {
return Uninterruptibles.getUninterruptibly(result);
} catch (Throwable t) {
}
}
return null;
不管是同步或异步方式,都会调用到loadFuture(key, loader)方法,先将数据取到。
loadFuture执行流程:
1、取key对应的旧值;
2、旧值不存在,则调用load方法取新值。
3、通过futureValue的set方法来设置新值,成功则返回futureValue,否则返回Futures.immediateFuture(newValue)。
4、旧值存在,则调用reload方法重取新值。
5、reload方法返回类型为ListenableFuture<V>,结果不为null则直接返回,否则返回 Futures.<V>immediateFuture(null)。
Futures的immediateFuture方法,返回了一个ImmediateSuccessfulFuture对象,在其构造函数中初始化值,该Future不能被取消或超时,它的isDone()方法只返回true。
异步方式,在取数完成后,将注册一个监听器,完成数据的最终加载并统计信息。
loadingFuture.addListener(
new Runnable() {
@Override
public void run() {
try {
V newValue = getAndRecordStats(key, hash, loadingValueReference, loadingFuture);
loadingValueReference.set(newValue);
} catch (Throwable t) {
logger.log(Level.WARNING, "Exception thrown during refresh", t);
loadingValueReference.setException(t);
}
}
}, sameThreadExecutor);
涉及的类:
这部分代码,通过Runnable接口实现,没有返回结果。
其中SettableFuture通过set(Object)或setException(Throwable)来设置值。
AbstractFuture类通过AbstractQueuedSynchronizer来处理同步问题和保证线程安全。所有涉及状态改变的方法,都会返回一个boolean值来表明状态改变是否成功,有效的状态有running, completed, failed, cancelled。这个类通过ExecutionList来保证当Future结束时所有注册的监听器,或完成后添加的监听器都能被执行。Runnable-Executor对存放在execution列表中,但不一定按它们添加的顺序执行,如果一个监听器在Future结束后添加,它将被立即执行,就算在它之前的监听器还没执行。
如果需要知道返回结果,可以使用Guava提供的ListenableFutureTask类,该类继承至FutureTask并实现了ListenableFuture接口, 可自由选择实现Runnable的void run()方法,或Callable的 V call()方法(有返回结果),这几个类看的不够仔细,需要再研究。