在官方的文档说明中,Guava Cache实现了三种加载缓存的方式:
- LoadingCache在构建缓存的时候,使用build方法内部调用CacheLoader方法加载数据
- 在使用get方法的时候,如果缓存不存在该key或者key过期等,则调用get(K, Callable)方式加载数据;
- 直接调用put方法来放置缓存
自然先看下核心实现localCache
在实现上,LocalCache的并发策略和ConcurrentHashMap的并发策略一致,也是进行了分段,支持不同段的并发写入。
https://www.cnblogs.com/shoren/p/guava_cache.html
guava cache是一个本地缓存。有以下优点:
- 很好的封装了get、put操作,能够集成数据源。
一般我们在业务中操作缓存,都会操作缓存和数据源两部分。如:put数据时,先插入DB,再删除原来的缓存;ge数据时,先查缓存,命中则返回,没有命中时,需要查询DB,再把查询结果放入缓存中。 guava cache封装了这么多步骤,只需要调用一次get/put方法即可。 - 线程安全的缓存,与ConcurrentMap相似,但前者增加了更多的元素失效策略,后者只能显示的移除元素。
- Guava Cache提供了三种基本的缓存回收方式:基于容量回收、定时回收和基于引用回收。定时回收有两种:按照写入时间,最早写入的最先回收;按照访问时间,最早访问的最早回收。
- 监控缓存加载/命中情况。
对于get(key, loader)方法流程:
- 对key做hash,找到存储的segment及数组table上的位置;
- 链表上查找entry,如果entry不为空,且value没有过期,则返回value,并刷新entry。
- 若链表上找不到entry,或者value已经过期,则调用lockedGetOrLoad。
- 锁住整个segment,遍历entry可能在的链表,查看数据是否存在是否过期,若存在则返回。若过期则删除(table,各种queue)。若不存在,则新建一个entry插入table。放开整个segment的锁。
- 锁住entry,调用loader的reload方法,从数据源加载数据,然后调用storeLoadedValue更新缓存。
- storeLoadedValue时,锁住整个segment,将value设置到entry中,并设置相关数据(入写入/访问队列,加载/命中数据等)。
getAll(keys)方法:
- 循环调用get方法,从缓存中获取key对应的value。没有命中的记录下来。
- 如果有没有命中的key,调用loadAll(keys,loader)方法加载数据。
- 将加载的数据依次缓存,调用segment的put(K key, int hash, V value, boolean onlyIfAbsent)方法。
- put时,锁住整个segment,将数据插入链表,更新统计数据。
put(key,value)方法:
- 对key做hash,找到segment的位置和table上的位置;
- 锁住整个segment,将数据插入链表,更新统计数据。
putAll(map) 循环调用put方法。
putIfAbsent(key, value) 缓存中,键值对不存在的时候才插入。