背景:
guava和caffine的refreshAfterWrite方法在用于本地缓存的场景是非常常用的,本文通过例子列举下caffine的refreshAfterWrite方法和guava的refreshAfterWrite的相同点和不同点
相同点/不同点:
以下都是使用key=XYZ作为例子
场景1:一开始缓存没有数据存在
1.1 caffine首次多线程获取key=XYZ的数据时,调用线程会去执行reload方法(如果没有显示定义,reload其实就是调用load方法)获取数据,而其他的线程会阻塞等待,对应代码BoundedLocalCache.doComputeIfAbsent方法如下:
1.2 guava首次多线程获取key=XYZ的数据时,调用线程会去执行reload方法(如果没有显示定义,reload其实就是调用load方法)获取数据,而其他的线程会阻塞等待,
也就是当首次加载key=XYZ的数据时,caffine和guava是一样的
场景二:当key=XYZ的数据已经存在
2.1 caffine多线程获取key=XYZ的数据时并假设已经大于等于刷新间隔时间时,这些线程都会获取到key=XYZ的旧值,不过此时会异步发起一个刷新操作,执行刷新操作的是默认线程池ForkJoinPool.commonPool(),也就是所有线程都不会被阻塞,但是他们的返回值都是上一轮的旧值
2.1 caffine多线程获取key=XYZ的数据时并假设已经大于等于刷新间隔时间时,这里又会分成两种场景
2.1.1 不提供自己的异步线程池,那么此时调用线程会被阻塞调用并返回新值,其余的线程直接返回旧值
2.1.2 业务提供线程池实现,那么此时所有线程都会返回旧值
注意:不论是caffine还是guava,他们触发refresh操作都是在真正去get获取数据的时候判断当前时间是否已经达到刷新时间点,如果是的话,对于caffine来说是通过执行ForkJoinPool.execute()方法执行reload,对于guava来说,调用线程直接调用reload方法/或者业务线程池使用线程池.execute执行reload方法,以上两者都不是我们通常意义上理解的类似ScheduleExecute.scheduleAtFixRatio的方法定时刷新的,他们不是按照固定频率由后台线程池主动触发执行的,而是由get获取操作触发的,只是说触发后由线程池来执行reload方法,这一点务必注意
总结:
由以上的不同点可知,如果一个key设定了刷新间隔比如1小时一次,但是这个key在一天的时间内并没有被访问,那么如果此时访问这个key,那么返回的key的值会是一天前刷新的那个旧值,并不是每小时更新的,切记, 只有这个key每小时都有被访问到才可以达到每小时都刷新的效果