类CacheBuilder
说明
注: 以下内容翻译自 类CacheBuilder 的javadoc
LoadingCache 和 Cache 实例的构建器,缓存有下列特性的任意组合:
- 自动装载到缓存
- 当超过最大数量时,LRU(least-recently-used/最近最少使用)移除
- 基于时间的超时机制,从最后一次访问或者最后一次写入开始测量
- key自动用弱引用(weak reference)包裹
- value自动用弱引用(weak reference)或者软(soft reference)引用包裹
- 移除通知(或者其他删除方式)
- 缓存访问统计
这些特性是可选的。可以使用他们中的所有或者都不来创建缓存。CacheBuilder默认创建的缓存不执行任何形式的移除。
使用案例:
LoadingCache<Key, Graph> graphs = CacheBuilder.newBuilder()
.maximumSize(10000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.removalListener(MY_LISTENER)
.build(
new CacheLoader<Key, Graph>() {
public Graph load(Key key) throws AnyException {
return createExpensiveGraph(key);
}
});
或者等价的:
//实际开发中这些将来自命令行或者配置文件
String spec = "maximumSize=10000,expireAfterWrite=10m";
LoadingCache<Key, Graph> graphs = CacheBuilder.from(spec)
.removalListener(MY_LISTENER)
.build(
new CacheLoader<Key, Graph>() {
public Graph load(Key key) throws AnyException {
return createExpensiveGraph(key);
}
});
返回的cache实现为hash table,和 ConcurrentHashMap 有类似的性能。它实现了 LoadingCache 和 Cache 接口的所有可选操作。asMap 视图(和集合视图)是"弱一致性迭代器(weakly consistent iterator)"。这意味着他们可以安全的并发使用,但是如果其他线程在迭代器创建之后修改缓存,哪些修改会影响到迭代器是未定义的。这些迭代器绝不会抛出 ConcurrentModificationException。
注意:默认,返回的cache使用相等比较(equals()方法)来检测key和value的相等性。但是,如果指定了 weakKeys(), cache将为key使用同一性(==)比较. 类似的,如果指定了 weakValues() 或者 softValues(), cache 为value使用同一性比较。
注: 相等比较是调用对象的equals()方法, 同一性比较是使用运算符==,直接比较对象的句柄。
当 maximumSize, maximumWeight, expireAfterWrite, expireAfterAccess, weakKeys, weakValues, 或者 softValues中的任何一个有要求时,实体将被从缓存中自动移除.
如果 maximumSize 或者 maximumWeight 被要求,实体可能在每次缓存修改时被移除。
如果要求 expireAfterWrite 或者 expireAfterAccess ,实体可能在每次缓存修改, 间歇的缓存访问,或者调用 Cache.cleanUp() 时被移除。过期的实体可能被 Cache.size() 计算,但是对于读写操作是绝对不可见。
如果要求 weakKeys, weakValues 或 softValues,这使得缓存中的key或者value可以被GC回收。key或者value被回收的实体可能在每次缓存修改,间歇的缓存访问,或者调用 Cache.cleanUp() 时从缓存中移除。过期的实体可能被 Cache.size() 计算,但是对于读写操作是绝对不可见。
某些缓存配置会导致周期性维护任务的增加, 这些任务在写操作或者没有写操作时在间歇的读操作期间被执行。返回的缓存的 Cache.cleanUp()方法也将执行维护操作,但是对于高吞吐量的缓存没有必要调用这个方法。只有构建时带有 removalListener, expireAfterWrite, expireAfterAccess, weakKeys, weakValues, 或 softValues 的缓存会执行周期性维护。
CacheBuilder 生成的缓存是可序列化的,而反序列化后的缓存保持所有的原始缓存的配置属性。注意序列化形式不包含缓存内容,仅有配置。
类定义
package com.google.common.cache;
public final class CacheBuilder<K, V> {}
- 范型
<K>
这个builder创建的所有缓存的key类型 - 范型
<V>
这个builder创建的所有缓存的value类型
类方法
创建builder
newBuilder()方法
public static CacheBuilder<Object, Object> newBuilder() {
return new CacheBuilder<Object, Object>();
}
newBuilder()方法使用默认设置构建一个新的 CacheBuilder 实例,包括 strong key, strong value,没有任何类型的自动移除。
注: 为什么这里类型是Object,而不是范型?
from()方法
public static CacheBuilder<Object, Object> from(CacheBuilderSpec spec) {
return spec.toCacheBuilder().lenientParsing();
}
public static CacheBuilder<Object, Object> from(String spec) {
return from(CacheBuilderSpec.parse(spec));
}
from()方法使用指定的设置来构建 CacheBuilder 实例。其中from(String spec)方法特别适合用于命令行或配置。
指定key和value比较的方式
keyEquivalence()方法
为key的比较设置自定义的相等策略:
CacheBuilder<K, V> keyEquivalence(Equivalence<Object> equivalence) {
checkState(keyEquivalence == null, "key equivalence was already set to %s", keyEquivalence);
keyEquivalence = checkNotNull(equivalence);
return this;
}
注:从代码上看,只容许设置一次equivalence,否则报错。
默认,如果指定weakKeys(),缓存将使用 Equivalence.identity 来检测key的相等性,否则使用Equivalence.equals。
具体实现的代码:
Equivalence<Object> getKeyEquivalence() {
//检查keyEquivalence
//如果没有设置就使用getKeyStrength().defaultEquivalence()
return MoreObjects.firstNonNull(keyEquivalence, getKeyStrength().defaultEquivalence());
}
Strength getKeyStrength() {
//如果没有设置keyStrength
//就取Strength.STRONG
return MoreObjects.firstNonNull(keyStrength, Strength.STRONG);
}
enum Strength {
STRONG {
Equivalence<Object> defaultEquivalence() {
//STRONG默认Equivalence.equals()
return Equivalence.equals();
}
WEAK {
Equivalence<Object> defaultEquivalence() {
//WEAK默认Equivalence.identity()
return Equivalence.identity();
}
};
valueEquivalence()方法
为value的比较设置自定义的相等策略:
CacheBuilder<K, V> valueEquivalence(Equivalence<Object> equivalence) {
checkState(valueEquivalence == null,
"value equivalence was already set to %s", valueEquivalence);
this.valueEquivalence = checkNotNull(equivalence);
return this;
}
注:从代码上看,只容许设置一次equivalence,否则报错。
默认,当指定 weakValues() 或者 softValues()时缓存使用 Equivalence.identity()来检测值的相等性,其他情况使用Equivalence.equals()。
指定缓存的基本配置
initialCapacity()方法
为内部的hash table设置最小的总大小,默认16。
例如,如果初始容量为 60, 而 concurrency level 是8,那么就会创建8个片段,每个片段有一个大小为8的hash table。在构建时刻提供一个足够的估算可以避免稍后昂贵的resize操作的需要,但是设置过大会浪费内存。
concurrencyLevel()方法
设置并发等级,指导更新操作期间容许的并发度。为了实现并发更新而不竞争,table内部进行分区。
理想情况,应该选择一个可以容纳同时并发修改table的线程数的值。使用一个明显过高的值会浪费空间和时间,而一个明显过低的值会导致线程竞争。
默认为4.
当前实现使用 concurrency level 来创建一个固定数量的 hashtable segment,每个segment由自己的写锁管理。片段锁在每次显式写时获取一次,而在每次缓存计算时获取两次(一次在装载新值之前,一次在装载完成之后)。很多内部的缓存管理在segment粒度上执行。例如,当要求有移除算法时,访问队列和写队列在每个segment上保持。
maximumSize()方法
指定缓存可以容纳的实体的最大数量。
注意:缓存可能在这个限制被超过之前移除实体。当缓存大小增长到接近最大值时,缓存移除最近不被再使用的实体。例如,缓存因为实体最近没有被使用或者不是很经常使用而移除这个实体。
当大小设置为0时,元素将在装载进入缓存之后被立即移除。测试中这个很有用,或者在不修改代码的情况下让缓存临时失效。
这个特性不能和 maximumWeight 同时使用。
maximumWeight()方法
指定缓存可以容纳的实体的最大重量。
使用 weigher()方法指定的 Weigher 来检测重量,而且使用这个方法要求在调用 build() 方法之前对应的调用 weigher() 方法。
注意:缓存可能在这个限制被超过之前移除实体。当缓存大小增长到接近最大值时,缓存移除最近不被再使用的实体。例如,缓存因为实体最近没有被使用或者不是很经常使用而移除这个实体。
当大小设置为0时,元素将在装载进入缓存之后被立即移除。测试中这个很有用,或者在不修改代码的情况下让缓存临时失效。
注意:重量(指maximumWeight()方法的weight参数)仅仅用于检测缓存是否超过容量。对于选择哪些实体移除没有影响。
weigher()
指定用于检测实体重量的 weigher。当决定哪个实体被移除时,实体重量被考虑到,而使用这个方法需要在调用build()之前对应调用maximumWeight(long)方法。当实体被插入到缓存时计算并记录重量,而之后在缓存实体的生存期内不变。
当实体的重量为0时,这个实体将不被基于大小的排除算法考虑(当然它还是可能被其他算法排除)。
非常重要:不再作为CacheBuilder实例返回this,这个方法返回 CacheBuilder
如果没有设置 weigher,则默认使用 OneWeigher.INSTANCE,将每个实体的重量简单返回1.
setKeyStrength()方法
指定缓存中的每个key(不是value)应该包裹为 WeakReference (默认,使用strong reference)。
警告: 当这个方法被使用时,得到的缓存将使用 identity comparison (==)来检测key的相等性。
key已经被GC的实体可能还被 Cache.size() 计数,但是对读或者写操作绝对不可见。
setValueStrength()和softValues()方法
指定缓存中的每个value(不是key)应该包裹为 WeakReference 或者 SoftReference (默认,使用strong reference)。
expireAfterWrite()方法
指定在实体创建或者最后一次覆盖值后,一旦固定期限达到,应该从缓存中自动删除。
当 duration 为0时,这个方法类似于 maximumSize(0),忽略其他指定的最大size或者weight。这可以用于测试,或者在不修改代码的情况下临时让缓存失效。
过期的实体可能还被 Cache.size() 计数,但是对读或者写操作绝对不可见。
expireAfterAccess()方法
指定在实体创建,最后一次覆盖值,或者最后一次访问后,一旦固定期限达到,应该从缓存中自动删除。
访问时间会被所有缓存读和写操作(包括Cache.asMap().get(Object)和Cache.asMap().put(K, V))重置,但是不包括 Cache.asMap() 的集合视图上的操作。
refreshAfterWrite()方法
指定在实体创建或者最后一次覆盖值后,一旦固定周期达到,活动实体有资格自动刷新。刷新的语义在方法 LoadingCache.refresh() 中指定,而且也是通过调用 CacheLoader.reload() 来执行的。
因为默认 CacheLoader.reload() 的实现是同步的,推荐这个方法的用户用一个异步实现覆盖 CacheLoader.reload() 方法,否则刷新将在不相关的缓存读和写操作期间执行。
目前自动刷新被实现为当实体的第一个陈腐的请求发生执行。这个触发刷新的请求将阻塞调用 CacheLoader.reload() 并立即返回新的值,如果返回的future是完成的,否则返回旧的值。
注意:在刷新期间抛出的异常都被记录日志然后吞掉。
注意:refreshAfterWrite()需要使用 LoadingCache。
removalListener()方法
指定监听器实例,缓存应该在每次实体因为任何 RemovalCause 原因被删除时通知。这个builder创建的每个缓存将调用这个监听器。
警告:在调用这个方法后,不要在继续使用 this cache builder引用,而是使用这个方法返回的引用。在运行开始,这些指向同一个实例,但是只有返回的引用拥有正确的范型类型来保证类型安全。
警告:监听器抛出的任何异常都将不会传播到缓存的用户,仅仅通过 Logger来记录日志。
recordStats()方法
开启在缓存操作期间的 CacheStats 累积。不调用这个方法则 Cache.stats() 将为所有统计返回0.注意记录统计需要为每次操作执行记录,而这将带来性能损失。
注意:在版本12.0之前,统计收集是自动开启的。
构建缓存实例的方法
build(CacheLoader)方法
构建一个缓存,要不返回已经装载的值,要不通过提供的 CacheLoader 自动计算或者获取值。如果其他线程正在装载这个key的值,简单等待这个线程完成并返回它装载的值。注意多个线程可以并发的装载不同的key。
这个方法不会改变在这个 CacheBuilder 实例的状态,因此可以再次调用来创建多个独立的缓存。
build()方法
构建一个缓存,当key被要求时不自动装载。
考虑使用 build(CacheLoader),如果可能实现 CacheLoader。
这个方法不会改变在这个 CacheBuilder 实例的状态,因此可以再次调用来创建多个独立的缓存。