本篇内容主要讲解“Guava cache如何构建无阻塞缓存”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Guava cache如何构建无阻塞缓存”吧!
guava cache是本地缓存,基于JDK ConcurrentHashMap 实现。不适合分布式环境使用(除非可以保证其节点缓存一致性如: 开源中国社区开源的 J2cache)。如果使用场景简单,没必要使用到缓存复杂特性可以使用ConcurrentHashMap ,比Guava cache 更高效直接。
构建方式有三种:CacheLoader,Callable,Inserted Directly。
CacheLoader:
适用于有一些合理的默认函数来加载或计算与密钥相关的值
Callable:
如果需要覆盖默认值,但仍需要原子“get-if-absent-compute”语义,则应使用get方法传递Callable接口。实现了:如果已缓存,返回;否则创建,缓存和返回。
Inserted Directly:
使用put方式直接插入值。
/** 自定义刷新缓存线程池 */
private static ListeningExecutorService backgroundRefreshPools =
MoreExecutors.listeningDecorator(executorService);
/** 创建缓存 */
public static final LoadingCache<String, String> cache = CacheBuilder.newBuilder()
.refreshAfterWrite(100,TimeUnit.MILLISECONDS)
.build(new CacheLoader<String, String>() {
@Override
public String load(String key) {
return getNewValue();
}
@Override
public ListenableFuture<String> reload(String key,String oldValue) {
return backgroundRefreshPools.submit(ReloadCache::getNewValue);
}
});
源码:
public ListenableFuture<V> loadFuture(K key, CacheLoader<? super K, V> loader) {
try {
stopwatch.start();
V previousValue = oldValue.get();
// 获取值为空时 加载load方法
if (previousValue == null) {
V newValue = loader.load(key);
return set(newValue) ? futureValue : Futures.immediateFuture(newValue);
}
// 否则执行reload
ListenableFuture<V> newValue = loader.reload(key, previousValue);
if (newValue == null) {
return Futures.immediateFuture(null);
}
return transform(
newValue,
new com.google.common.base.Function<V, V>() {
@Override
public V apply(V newValue) {
LoadingValueReference.this.set(newValue);
return newValue;
}
},
directExecutor());
} catch (Throwable t) {
ListenableFuture<V> result = setException(t) ? futureValue : fullyFailedFuture(t);
if (t instanceof InterruptedException) {
Thread.currentThread().interrupt();
}
return result;
}
}
当CacheBuilder.refreshAfterWrite(java.time.Duration)或通过调用LoadingCache.refresh(K)刷新现有缓存条目时,将调用此方法。
@GwtIncompatible
public ListenableFuture<V> reload(K key, V oldValue)throws Exception
参数:
key - 应该加载其值的非null键
oldValue - 与key对应的非null旧值
返回值:
不为null的ListenableFuture,不会返回null
异常:
Exception - 如果无法重新加载结果
注意事项:
1、refresh 会“吞掉”缓存更新时的异常操作。依然使用旧值返回。
2、可以使用CacheBuilder.refreshAfterWrite(long,TimeUnit)将自动定时刷新添加到缓存中。
与expireAfterWrite相反,refreshAfterWrite将使键在指定的持续时间后符合刷新条件,但只有在查询条目时才会实际刷新。
(如果将CacheLoader.reload实现为异步,则刷新不会减慢查询速度。)因此,例如,您可以在同一缓存上指定refreshAfterWrite和expireAfterWrite,以便条目上的到期计时器不会每当条目符合刷新条件时,都会盲目重置,因此如果条目在符合刷新条件后未被查询,则允许条目过期。expireAfterWrite 和 refreshAfterWrite 不建议一起使用。
3、refreshAfterWrite 构建缓存更适合应对,热点缓存问题。由于总有旧值返回是一种托底方案。
4、guava cache 刷新功能很多基于其并发工具类Futures提供的线程回调功能。
当没有guava cache 对缓存可使用 ScheduledThreadPoolExecutor + ConcurrentHashMap
注意:对异常的处理,否则导致线程的终止。
参考资料:guava cache wiki https://github.com/google/guava/wiki/CachesExplained
guava cache api docs https://google.github.io/guava/releases/snapshot-jre/api/docs/
到此,相信大家对“Guava cache如何构建无阻塞缓存”有了更深的了解,不妨来实际操作一番吧!这里是亿速云网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。