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方式直接插入值。

CacheLoader构建无阻塞缓存:

/** 自定义刷新缓存线程池 */

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;

}

}

reload java doc

当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/

转载于:https://my.oschina.net/u/3418748/blog/3093831

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值