Guava-Cache
Guava-Cache
缓存是日常开发中经常应用到的一种技术手段,合理的利用缓存可以极大的改善应用程序的性能。 缓存在各种各样的用例中非常有用。例如,当计算或检索值很昂贵时,您应该考虑使用缓存,并且不止一次需要它在某个输入上的值。
maven 依赖
1 | <dependency> |
Guava Cache与ConcurrentMap很相似,但也不完全一样。最基本的区别是ConcurrentMap会一直保存所有添加的元素,直到显式地移除。相对地,Guava Cache为了限制内存占用,通常都设定为自动回收元素。在某些场景下,尽管LoadingCache 不回收元素,它也是很有用的,因为它会自动加载缓存。
加载
Guava Cache有两种缓存加载的方式:CacheLoader 和 Callable,这两种方式都是按照”获取缓存-如果没有-则计算”[get-if-absent-compute]的规则加载的。不同的是,CacheLoader是在创建Cache的时候,实现了一个统一的根据key获取value的方法,而Callable更加灵活,允许你在get的时候指定一个callable来获取value(get(K, Callable)
)
- CacheLoader
创建CacheLoader,只需要实现load方法,如下面代码示例。当我们调用get方法获取指定key的缓存值时,如果key的value值还没有缓存,Guava Cache则会自动调用load方法加载value值到缓存并返回。
1 | LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder() |
从LoadingCache查询的正规方式是使用get(K)
方法。这个方法要么返回已经缓存的值,要么使用CacheLoader向缓存原子地加载新值。由于CacheLoader可能抛出异常,LoadingCache.get(K)也声明为抛出ExecutionException异常。
Callable
所有类型的Guava Cache,不管有没有自动加载功能,都支持get(K, Callable)方法。这个方法返回缓存中相应的值,或者用给定的Callable运算并把结果加入到缓存中。在整个加载方法完成前,缓存项相关的可观察状态都不会更改。这个方法简便地实现了模式”如果有缓存则返回;否则运算、缓存、然后返回”。
1 | Cache<String, String> cache = CacheBuilder.newBuilder() |
显式插入
使用cache.put(key, value)
方法可以直接向缓存中插入值,这会直接覆盖掉该key之前缓存的值。使用Cache.asMap()
视图提供的任何方法也能相应的修改缓存。但是,asMap视图的任何方法都不能保证缓存项被原子地加载到缓存中。
缓存回收
因为我们的数据是缓存在java堆内存上的,存储容量受到堆内存大小限制。当我们缓存的数据量很大时,会影响到GC,所以缓存必须要有回收策略。Guava Cache提供三种回收方式:
基于容量回收
通过CacheBuilder.maximumSize(long)
设置缓存项的最大数目,当达到最大数目后,继续添加缓存项,Guava Cache会根据LRU策略回收缓存项来保证不超过最大数目。
另外,可以通过CacheBuilder.weigher(Weigher)
设置不同缓存项的权重,Guava Cache根据权重来回收缓存项。
1 | LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder() |
定时回收
CacheBuilder提供两种定时回收的方法:
expireAfterAccess(long, TimeUnit)
:缓存项在给定时间范围内没有读/写访问,那么下次访问时,会被回收,然后同步load()
(一个线程去load,其他线程等待)。
expireAfterWrite(long, TimeUnit)
:缓存项在给定时间范围内没有写访问,那么下次访问时,会被回收,然后同步load()
(一个线程去load,其他线程等待)。
Guava Cache不会专门维护一个线程来回收这些过期的缓存项,只有在读/写访问时,才去判断该缓存项是否过期,如果过期,则会回收。
基于引用回收
通过使用弱引用的键、或弱引用的值、或软引用的值,Guava Cache可以把缓存设置为允许垃圾回收:
CacheBuilder.weakKeys()
:使用弱引用存储键。当键没有其它(强或软)引用时,缓存项可以被垃圾回收。因为垃圾回收仅依赖恒等式(),使用弱引用键的缓存用而不是equals比较键。CacheBuilder.weakValues()
:使用弱引用存储值。当值没有其它(强或软)引用时,缓存项可以被垃圾回收。因为垃圾回收仅依赖恒等式(),使用弱引用值的缓存用而不是equals比较值。CacheBuilder.softValues()
:使用软引用存储值。软引用只有在响应内存需要时,才按照全局最近最少使用的顺序回收。考虑到使用软引用的性能影响,我们通常建议使用更有性能预测性的缓存大小限定(见上文,基于容量回收)。使用软引用值的缓存同样用==而不是equals比较值。
显式清除
1 | 任何时候,你都可以显式地清除缓存项,而不是等到它被回收: |
- 个别清除:
Cache.invalidate(key)
- 批量清除:
Cache.invalidateAll(keys)
- 清除所有缓存项:
Cache.invalidateAll()
移除监听器
通过CacheBuilder.removalListener(RemovalListener),你可以声明一个监听器,以便缓存项被移除时做一些额外操作。缓存项被移除时,RemovalListener会获取移除通知RemovalNotification其中包含移除原因RemovalCause键和值。
1 | CacheLoader<Key, DatabaseConnection> loader = new CacheLoader<Key, DatabaseConnection> () { |
Author: corn1ng
Link: https://corn1ng.github.io/2019/09/04/Guava-Cache/
License: 知识共享署名-非商业性使用 4.0 国际许可协议