一、使用
1)简单初始化:
LoadingCache<Long, Long> loadCache = CacheBuilder.newBuilder()
.expireAfterWrite(6, TimeUnit.SECONDS)
.maximumSize(100)
.build(new CacheLoader<Long, Long>() {
@Override
public Long load(Long userId) throws Exception {
return getNum(userId);
}
});
2)异步更新的初始化:
LoadingCache<Long, Long> loadCache = CacheBuilder.newBuilder()
.expireAfterWrite(6, TimeUnit.SECONDS)
.refreshAfterWrite(2, TimeUnit.SECONDS)
.maximumSize(100)
.build(CacheLoader.asyncReloading(new CacheLoader<Long, Long>() {
@Override
public Long load(Long userId) throws Exception {
return getNum(userId);
}
},Executors.newFixedThreadPool(3)));
二、源码
首先,LoadingCache是由调用触发的刷新值,而并不会定时调度主动触发。所以在请求的时候顺便验证当前值是否有效期超过refreshNanos(即refreshAfterWrite的值),是则调用refresh,这时候更新操作会标记一下状态,以便于其他后续的线程调用refresh时,不重复创建。然后具体refresh操作执行,根据是否第一次区分调用load或reload,这个见下文分析。
1)get方法:因为build里面使用是LocalLoadingCache类,所以这里看它的源码。
最终调用时Segment.get方法:
V get(K key, int hash, CacheLoader<? super K, V> loader) throws ExecutionException {
checkNotNull(key);
checkNotNull(loader);
try {
if (count != 0) { // read-volatile
// don't call getLiveEntry, which would ignore loading values
ReferenceEntry<K, V> e = getEntry(key, hash);
if (e != null) {
long now = map.ticker.read();
V value = getLiveValue(e, now);
if (value != null) {
recordRead(e, now);
statsCounter.recordHits(1);
return scheduleRefresh(e, key, hash, value, now, loader);
}
ValueReference<K, V> valueReference = e.getValueReference();
if (valueReference.isLoading()) {
return waitForLoadingValue(e, key, valueReference);
}
}
}
// at this point e is either null or expired;
return lockedGetOrLoad(key, hash, loader);
} catch (ExecutionException ee) {
Throwable cause = ee.getCause();
if (cause instanceof Error) {
throw new ExecutionError((Error) cause);
} else if (cause instanceof RuntimeException) {
throw new UncheckedExecutionException(cause);
}
throw ee;
} finally {
postReadCleanup();
}
}
2)scheduleRefresh方法:
public ListenableFuture<V> loadFuture(K key, CacheLoader<? super K, V> loader) {
stopwatch.start();
V previousValue = oldValue.get();
try {
if (previousValue == null) {
V newValue = loader.load(key);
return set(newValue) ? futureValue : Futures.immediateFuture(newValue);
}
ListenableFuture<V> newValue = loader.reload(key, previousValue);
if (newValue == null) {
return Futures.immediateFuture(null);
}
// To avoid a race, make sure the refreshed value is set into loadingValueReference
// *before* returning newValue from the cache query.
return Futures.transform(newValue, new Function<V, V>() {
@Override
public V apply(V newValue) {
LoadingValueReference.this.set(newValue);
return newValue;
}
});
} catch (Throwable t) {
if (t instanceof InterruptedException) {
Thread.currentThread().interrupt();
}
return setException(t) ? futureValue : fullyFailedFuture(t);
}
}