自命为缓存之王的Caffeine(2)

时间:2023-02-09 19:00:57

您好,我是湘王,这是我的51CTO博客,欢迎您来,欢迎您再来~




缓存除了过期策略,其实还有刷新和填充策略。刷新策略就是当数值变化时怎么处理,而所谓填充策略,就是将数据保存到缓存的方式。

Caffeine提供了三种刷新方法:

1、基于类Caffeine的refreshAfterWrite(time, duration)方法;

2、基于接口LoadingCache的refresh(key)方法;

3、基于CacheLoader的reload(K, V)。

通过类的Caffeine.refreshAfterWrite()方法实现更新:

自命为缓存之王的Caffeine(2)


refreshAfterWrite()方法是一种「被动」更新,它必须设置CacheLoad,key过期后并不立即刷新value:

1、当过期后第一次调用get()方法时,得到的仍然是过期值;

2、当过期后第二次调用get()方法时,才会得到更新后的值。

这感觉有点bug。

第二种是通过接口的LoadingCache.refresh(Key)方法实现更新:

自命为缓存之王的Caffeine(2)


而refresh(Key)方法只要调用了,当再次读取时,就是最新的值(这一点和refreshAfterWrite()方法是有区别的)。

最后一种刷新策略有两类情况,一是Cache类覆盖CacheLoader.reload(K, V)方法,二是LoadingCache类覆盖CacheLoader.reload(K, V)方法:

自命为缓存之王的Caffeine(2)


自命为缓存之王的Caffeine(2)


可以比较使用Cache和LoadingCache,看看其中的细微差别。而使用不同的Cache涉及到Caffeine另一个方面:缓存填充。Caffeine提供了三种「填充」方法:

1、手动;

2、同步;

3、异步。

自命为缓存之王的Caffeine(2)


自命为缓存之王的Caffeine(2)


自命为缓存之王的Caffeine(2)


它们的区别在于:

1、手动方式可以显式地控制检索、更新和删除条目,控制起来比较方便,适用于大多数场景;

2、异步方式由于需要使用组合异步CompletableFuture,很明显是用于保存需要占用较大内存的对象,且这类保存操作比较费时;

3、同步方式介于手动和异步之间,适用于需要从外部加载数据源,或者需要保存全局变量的情况(其实get(key, k -> v)方法更适合);

4、从之前的操作来看,同步方式对refresh(Key)方法支持得很好,而且get()、get(key, k -> v)和getIfPresent()对结果也有影响;

5、这有点类似于排列组合:不同的填充策略 + 不同的get()方法 + 不同的刷新策略,就会产生不同的结果,这需要开发者对Caffeine有足够多的使用和了解,才能完全驾驭。

最后的一个问题是:Caffeine的get(key, k -> v)与getIfPresent()到底有啥不同?

通过阅读Caffeine源码了解到:

1、LoadingCache接口只有get(key)方法,继承于Cache;

2、Cache接口有get(key, k -> v)方法;

3、AsyncCache接口也有get(key, k -> v)方法。

不管是哪种填充方式,都有get(key, k -> v)方法。

get(key, k -> v)方法是以阻塞方式调用,在多个线程环境下,只会调用一次Function函数,这可以避免与其他线程的写入竞争,因此get(key, k -> v)优于getIfPresent()。

建议使用get(key, k -> v)方法,更安全,更通用。





感谢您的大驾光临!咨询技术、产品、运营和管理相关问题,请关注后留言。欢迎骚扰,不胜荣幸~