guava cache 异步刷新源码解析

2022-01-12 14:58:51   最后更新: 2022-01-12 14:58:51   访问数量:17




  1. 背景

 

上一篇文章中,我们详细介绍了 guava cache 的使用方法,尤其是在其中重点介绍了 guava cache 异步回种的用法:

 

那么,性能优异的异步回种缓存究竟是如何实现的呢?本文我们就来详细阅读 guava cache 的完整流程代码,抽丝剥茧,学习其中的思想与智慧。

 

 

guava cache 异步回种基本思想:

 

 

 

cache.get() 完整流程图:

 

 

 

 

 

 

如图所示,下述场景下,缓存直接通过加锁同步调用的方式获取数据:

 

  1. 缓存为空;

 

  1. 缓存中获取数据为空;

 

  1. 缓存中数据已超过 expireTime 并且状态不是 loading 状态。

 

 

guava cache 在上述情况下调用 lockedGetOrLoad() 方法获取数据,这个方法中,通过加 AQS 锁避免多个线程同时 load 获取数据,然后通过回调 load() 方法同步获取数据。

 

 

如果第一步操作中,缓存中数据获取正常,没有超过 expireTime,那么就需要判断是否过了 refreshTime 了。

 

expireTime 与 refreshTime 的区别如下:

 

  1. 数据一旦超过 expireTime,则说明数据不可用,已经是失效数据。

 

  1. 数据如果没有超过 expireTime,但超过了 refreshTime,说明数据虽然没有失效,但是需要尝试从数据源获取并刷新数据。

 

 

 

 

如图所示,scheduleRefresh() 方法的流程并不复杂:

 

  1. 判断是否已经超过 refreshTime,如果已经超过,则执行 refresh 方法尝试获取新数据;

 

  1. 新数据获取成功,则返回新数据,否则返回 oldValue。

 

 

 

上述流程中,refresh() 方法具体执行的流程就是 loadAsync() 方法,如图所示:

 

 

 

loadAsync 方法分两种情况:

 

  1. 同步 load:当旧值不存在时,执行同步 load 方法获取数据,保证数据返回;

 

  1. 异步 reload:当旧值存在时,执行异步 reload 方法获取数据,保证耗时可控。

 

 

无论是通过同步 load 方法还是异步 reload 方法获取数据,都会最终封装成 Future 对象,并且为这个 Future 对象添加 Listener,从而能够在数据获取完成后,设置缓存及更新缓存中数据的状态。

 

 

了解了 guava cache 异步回种的基本思想,也许你会觉得这一套解决方案的实现是如此简单,那么,我们知道,memcache、redis 都是只有提供了同步接口的,那么,你是否可以在此基础上实现一套异步回种方案呢?

 

 

欢迎关注微信公众号,以技术为主,涉及历史、人文等多领域的学习与感悟,每周三到七篇推文,只有全部原创,只有干货没有鸡汤

 

java 专题






技术分享      缓存      java      cache      guava      容错      稳定性     


京ICP备2021035038号