这是我参与「第三届青训营 -后端场」笔记创作活动的第5篇笔记
源码分析
1、数据的操作
Cache
Cache定义了一些操作数据的方法,类似一个Map的抽象。
它主要的方法有三个:
-
get,传入key和一个方法。
- 如果缓存中存在这个key的键值,就返回对应的值
- 如果缓存中不存在,就调用该方法获得数据,缓存并返回
-
put,传入key、value。
- 如果key不存在,就保存这个键值对
- 如果key存在,就用新的value替换旧的value
-
invalidate,传入key
- 从缓存中删除该key的键值对
它要求实现类是线程安全的。
caffeine保证原子性的原理很简单,它对数据的存储是利用了currentHashMap。
它对数据的操作也是利用了currentHashMap提供的线程安全方法。
LoadingCache
LoadingCache继承自Cache。
它额外扩展了三个方法:get、getAll、refresh。
这个类的思想是,自动缓存。即如果缓存未命中,可以调用传入的方法,去获取到数据,缓存并返回。
LocalManualCache
LocalManualCache继承自Cache。
这个类的思想是,手动管理缓存,即只能自己手动去增删查,在缓存未命中时不能调用builder的方法去获取数据。
做法是,内部引入了一个LocalCache集合,对Cache的put、get方法做了默认实现,默认实现就是从LocalCache中存取数据。
LocalManualCache接口主要有两个实现类,决定了它使用哪种缓存类型。
- UnboundedLocalManualCache
- BoundedLocalManualCache
LocalLoadingCache
LocalLoadingCache,提供了LoadingCache接口的骨架实现,降低了LocalCache实现类的代码量
两种缓存模式的解析
要创建一个缓存对象,有两种类型:LocalManualCache手动缓存、LoadingCache自动缓存。
自动缓存的特点是,如果缓存中没有对应的数据,可以通过调用CacheLoader的load方法来获取数据。
Cache类型的对象使用建造者模式来创建。
其中,如果给build方法传入了一个CacheLoader对象,就会返回一个LoadingCache自动缓存。
否则,就会返回一个LocalManualCache手动缓存
2、数据的存储
提供了一个LocalCache接口,是对JDK的ConcurrentMap接口的扩展。
它提供了数据的存取方法。
LocalCache有两个实现类,区别主要是缓存淘汰策略
- UnboundedLocalCache,它底层使用了一个ConcurrentHashMap,容量是builder中传入的缓存容量。没有缓存淘汰策略
- BoundedLocalCache,是一个抽象类。它是一个有回收策略的缓存实现,不同的策略对应不同的实现类。
这个的思想是,有界缓存和无界缓存。无界缓存的容量是无限的,而有界缓存的容量是固定的,所以必须设置缓存的淘汰策略。
3、缓存淘汰策略
每个回收策略都对应一个BoundedLocalCache的具体实现类,这些实现类都由LocalCacheFactory来构造。
LocalCacheFactory中只有一个方法,newBoundedLocalCache(),它会返回一个BoundedLocalCache的实现类。
它的逻辑大概是这样的:
检查传入的builder中的各项配置,拼接字符串,最终得到一个全类名。
caffeine为每种情况都编写了对应的实现类,所以这个全类名就可以对应到一个符合用户配置的实现类。
这种穷举性的做法,是因为caffeine对每种情况都做了优化。
有了全类名,就可以通过类加载器,把类加载进来。