1. 简介
Guava Cache是指在JVM的内存中缓存数据,相比较于传统的数据库或redis存储,访问内存中的数据会更加高效,无网络开销。
根据Guava官网介绍,下面的这几种情况可以考虑使用Guava Cache:
1. 愿意消耗一些内存空间来提升速度。
2. 预料到某些键会被多次查询。
3. 缓存中存放的数据总量不会超出内存容量。
因此,Guava Cache特别适合存储那些访问量大、不经常变化、数据量不是很大的数据,以改善程序性能。
2. 类图
Guava Cache的类图中,主要涉及了5个类:CacheBuilder、LocalCache、Segment、EntryFactory和ReferenceEntry,大部分业务逻辑都在前面三个类,依次介绍如下:
2.1 CacheBuilder
CacheBuilder是一个用于构建Cache的类,是建造者模式的一个例子,主要的方法有:
- maximumSize(long maximumSize): 设置缓存存储的所有元素的最大个数。
- maximumWeight(long maximumWeight): 设置缓存存储的所有元素的最大权重。
- expireAfterAccess(long duration, TimeUnit unit): 设置元素在最后一次访问多久后过期。
- expireAfterWrite(long duration, TimeUnit unit): 设置元素在写入缓存后多久过期。
- concurrencyLevel(int concurrencyLevel): 设置并发水平,即允许多少线程无冲突的访问Cache,默认值是4,该值越大,LocalCache中的segment数组也会越大,访问效率越高,当然空间占用也大一些。
- removalListener(RemovalListener<? super K1, ? super V1> listener): 设置元素删除通知器,在任意元素无论何种原因被删除时会调用该通知器。
- setKeyStrength(Strength strength): 设置元素的key是强引用,还是弱引用,默认强引用,并且该属性也指定了EntryFactory使用是强引用还是弱引用。
- setValueStrength(Strength strength) : 设置元素的value是强引用,还是弱引用,默认强引用。
2.2 LocalCache
LocalCache是一个支持并发访问的Hash Map,它实现了ConcurrentMap,其内部会持有一个segment数组,元素的增删改查都是通过调用segment的对应方法来实现的,
其主要的方法有:
- get(Object key): 查询一个key,内部实现是调用了Segment的get方法。
- public V put(K key, V value): 添加一个对象到cache中,内部实现是调用了Segment的put方法。
- remove(Object key) : 删除一个key,内部实现是调用了Segment的remove方法。
- replace(K key, V value):更新一个key,内部实现是调用了Segment的update方法。
2.3 Segment
segment是实际元素的持有者,它内部持有一个table数组,数组的每个元素又对应一个链表,链表上则保存了实际的元素,它的主要方法对应LocalCache提供的增删改查的接口,这里就不再啰嗦了。
2.4 EntryFactory
EntryFactory是entry的创建工厂,可支持创建强引用、弱引用、强读引用、强写引用、强读写引用、弱读引用、弱写引用、弱读写引用等类型的元素。
强引用和弱引用就是java四种引用类型里面的强弱引用,默认是强引用,而读引用是指创建的元素会记录最后一次的访问时间,如果用户在CahceBuilder中调用了expireAfterAccess或者maximumWeight则会使用读引用类型的工厂,写引用类型也是同样的逻辑。
2.5 ReferenceEntry
ReferenceEntry是元素的接口定义,它的实现类就是EntryFactory中创建的元素,包含了8种类型的元素,元素中至少包含了key、value和hash三个字段,其中hash是当前元素的hash值,如果是读引用则会多一个accessTime字段,以强引用的构造方法为例:
static class StrongEntry<K, V> extends AbstractReferenceEntry<K, V> { final K key; StrongEntry(K key, int hash, @Nullable ReferenceEntry<K, V> next) { this.key = key; this.hash = hash; this.next = next; } @Override public K getKey() { return this.key; } // The code below is exactly the same for each entry type. final int hash; final @Nullable ReferenceEntry<K, V> next; volatile ValueReference<K, V> valueReference = unset();