集合体系

Multimap

https://www.yuque.com/docs/share/28230865-8398-40d4-9041-36ff73fa67c8?# 《guava中Multimap、HashMultimap使用详解》

Guava Cache

Guava Cache的实现是基于ConcurrentHashMap 的

(一)Ehcache与Guava Cache的区别

https://www.yuque.com/docs/share/6c7e8c49-6d09-4446-b43b-25b9e1a3cfe1?#

(二)清除缓存的策略


任何Cache的容量都是有限的,而缓存清除策略就是决定数据在什么时候应该被清理掉。GuavaCache提了以下几种清除策略:

基于存活时间的清除(Timed Eviction)
这应该是最常用的清除策略,在构建Cache实例的时候,CacheBuilder提供两种基于存活时间的构建方法:
(1)expireAfterAccess(long, TimeUnit):缓存项在创建后,在给定时间内没有被读/写访问,则清除。
(2)expireAfterWrite(long, TimeUnit):缓存项在创建后,在给定时间内没有被写访问(创建或覆盖),则清除。
expireAfterWrite()方法有些类似于redis中的expire命令,但显然它只能设置所有缓存都具有相同的存活时间。若遇到一些缓存数据的存活时间为1分钟,一些为5分钟,那只能构建两个Cache实例了。

基于容量的清除(size-based eviction)
在构建Cache实例的时候,通过CacheBuilder.maximumSize(long)方法可以设置Cache的最大容量数,当缓存数量达到或接近该最大值时,Cache将清除掉那些最近最少使用的缓存。
以上是这种方式是以缓存的“数量”作为容量的计算方式,还有另外一种基于“权重”的计算方式。比如每一项缓存所占据的内存空间大小都不一样,可以看作它们有不同的“权重”(weights)。你可以使用CacheBuilder.weigher(Weigher)指定一个权重函数,并且用CacheBuilder.maximumWeight(long)指定最大总重。

显式清除
任何时候,你都可以显式地清除缓存项,而不是等到它被回收,Cache接口提供了如下API:
(1)个别清除:Cache.invalidate(key)
(2)批量清除:Cache.invalidateAll(keys)
(3)清除所有缓存项:Cache.invalidateAll()


基于引用的清除(Reference-based Eviction)
在构建Cache实例过程中,通过设置使用弱引用的键、或弱引用的值、或软引用的值,从而使JVM在GC时顺带实现缓存的清除,不过一般不轻易使用这个特性。
(1)CacheBuilder.weakKeys():使用弱引用存储键
(2)CacheBuilder.weakValues():使用弱引用存储值
(3)CacheBuilder.softValues():使用软引用存储值

什么时候发生清除




这在GuavaCache被称为“延迟删除”,即删除总是发生得比较“晚”,这也是GuavaCache不同于其他Cache的地方!这种实现方式的问题:缓存会可能会存活比较长的时间,一直占用着内存。如果使用了复杂的清除策略如基于容量的清除,还可能会占用着线程而导致响应时间变长。但优点也是显而易见的,没有启动线程,不管是实现,还是使用起来都让人觉得简单(轻量)。
如果你还是希望尽可能的降低延迟,可以创建自己的维护线程,以固定的时间间隔调用Cache.cleanUp(),ScheduledExecutorService可以帮助你很好地实现这样的定时调度。不过这种方式依然没办法百分百的确定一定是自己的维护线程“命中”了维护的工作。


详细说明:
https://www.yuque.com/docs/share/f60682a3-c048-42f5-ad32-e3b7e7b00ef5?#


注意点

请一定要记住GuavaCache的实现代码中没有启动任何线程!!Cache中的所有维护操作,包括清除缓存、写入缓存等,都是通过调用线程来操作的。这在需要低延迟服务场景中使用时尤其需要关注,可能会在某个调用的响应时间突然变大。
GuavaCache毕竟是一款面向本地缓存的,轻量级的Cache,适合缓存少量数据。如果你想缓存上千万数据,可以为每个key设置不同的存活时间,并且高性能,那并不适合使用GuavaCache。


(三)CacheBuilder


CacheBuilder:LoadingCache实现类,存储键值形式(本质Map)
最常用的方法的介绍—————源码中对于各个方法的解释:
① 定义对象个数的限制,如果没有定义过期规则则超出限制的时候,会将最远时间最少使用的自动过期保证总数在设置的范围内

public CacheBuilder maximumSize(long size)
②定义过期时间显示(duration:设置持续时间;unit:持续时间单位【备注:右下插图为可用单位】):缓存中数值在指定时间没有值更新会过期

public CacheBuilder expireAfterWrite(long duration, TimeUnit unit)
③同样是定义过期时间,但是这个和上面的有点区别,只要缓存中的数据被访问就会重置数据的缓存时间

public CacheBuilder expireAfterAccess(long duration, TimeUnit unit)


newBuilder 创建一个默认的配置的CacheBuilder
from 根据规定的配置信息去初始化CacheBuilder实例
initialCapacity 根据场景去设置内部hash表的内存
concurrencyLevel 设置并发数,假如设置为5,代表同一个时间最多只能有5个线程往Cache执行写入操作
maximumSize 方法可以设置Cache的最大容量数,当缓存数量达到或接近该最大值时,Cache将清除掉那些最近最少使用的缓存。
maximumWeight 指定最大总重。
weigher 指定一个权重函数
weakKeys 使用弱引用存储键
weakValues 使用弱引用存储值
softValues 使用软引用存储值
expireAfterWrite 设置cache中的数据在写入之后的存活时间,缓存项在创建后,在给定时间内没有被写访问(创建或覆盖),则清除。
expireAfterAccess 缓存项在创建后,在给定时间内没有被读/写访问,则清除。
refreshAfterWrite
ticker
removalListener
recordStats
build
build

(四)Cache

/
该接口的实现被认为是线程安全的,即可在多线程中调用
通过被定义单例使用
*/
public interface Cache {

/

通过key获取缓存中的value,若不存在直接返回null
/
V getIfPresent(Object key);

/
通过key获取缓存中的value,若不存在就通过valueLoader来加载该value
整个过程为 “if cached, return; otherwise create, cache and return”
注意valueLoader要么返回非null值,要么抛出异常,绝对不能返回null
/
V get(K key, Callable<?
extends V> valueLoader) throws ExecutionException;

/

添加缓存,若key存在,就覆盖旧值
/
void put(K key, V value);

/
删除该key关联的缓存
/
void invalidate(Object key);

/

删除所有缓存
/
void invalidateAll();

/
执行一些维护操作,包括清理缓存
/
void** cleanUp();
}