WeakCache 的作用看名字就知道,是一个缓存类,那它到底缓存什么类型的对象呢? 先从 WeakCache 注解开始:
它是一个具有二级缓存的弱引用类,一级缓存的 key 和 value 都是弱引用,二级缓存都是强引用。其中 key 是根据入参直接传入的,二级缓存的 key 和v alue 都是是根据一级缓存的 key 和 value 通过各自的工厂方法(subKeyFactory 和 valueFactory)计算得到的。一级 key 可以为空,并且当二级缓存的 key 和 value 不为空时,它可以来做比较。二级缓存 key 可以用它们各自的 equals 方法做比较。当弱引用被 clear 后,entries 会被以惰性(lazily)方式被删除,被 clear 过后的弱引用的 value 值不会被删除,但是它们会在逻辑上被置为不存在的,并且要重新计算二级缓存的 key 和 value。
看了上面的注解翻译一脸懵逼,接下来慢慢分析。
先看下对象属性定义
private final ReferenceQueue<K> refQueue = new ReferenceQueue<>();
// the key type is Object for supporting null key
private final ConcurrentMap<Object, ConcurrentMap<Object, Supplier<V>>> map
= new ConcurrentHashMap<>();
private final ConcurrentMap<Supplier<V>, Boolean> reverseMap
= new ConcurrentHashMap<>();
private final BiFunction<K, P, ?> subKeyFactory;
private final BiFunction<K, P, V> valueFactory;
- refQueue:引用队列,存放被回收的 WeakReference。
- map:一、二级缓存容器,而一级缓存的 key、value 是弱引用类型对象,二级缓存的 key、value 则为强引用对象。
- reverseMap:记录已注册的 Supplier。
- subKeyFactory valueFactory:二级缓存的 key、value 生成器。
了解了对象属性之后,接下来看下定义的几个内部类的具体意义所在:
Value
继承 Supplier 接口(Supplier<T> 接口没有入参,返回一个 T 类型的对象,结果的提供者,类似工厂方法),通用的 reference 引用对象的提供者,通过该 reference 来获取引用的底层实例。
private interface Value<V> extends Supplier<V> {}
LookupValue
实现 Value 接口,并重写了 hashCode 和 equals 方法,在 equals 方法中通过判断底层引用的实例是否相等为依据:
private static final class LookupValue<V> implements Value<V> {
private final V value;
LookupValue(V value) {
this.value = value;
}
@Override
public V get() {
return value;
}
@Override
public int hashCode() {
return System.identityHashCode(value); // compare by identity
}
@Override
public boolean equals(Object obj) {
return obj == this ||
obj instanceof Value &&
this.value == ((Value<?>) obj).get(); // compare by identity
}
}
CacheValue
对象的弱引用类型,其主要目的是判断不同的 CacheValue 是否引用的是同一个底层对象,即 equals 方法实现目的:
private static final class CacheValue<V>
extends WeakReference<V> implements Value<V>
{
private final int hash;
CacheValue(V value) {
super(value);
this.hash = System.identityHashCode(value); // compare by identity
}
@Override
public int hashCode() {
return hash;
}
@Override
public boolean equals(Object obj) {
V value;
return obj == this ||
obj instanceof Value &&
// cleared CacheValue is only equal to itself
(value = get()) != null &&
value == ((Value<?>) obj).get(); // compare by identity
}
}
CacheKey
key 的弱引用类。一级缓存 key 对象类型。
private static final class CacheKey<K> extends WeakReference<K> {
// 使用Object实例替换null
private static final Object NULL_KEY = new Object();
static <K> Object valueOf(K key, ReferenceQueue<K> refQueue) {
return key == null
// 使用NULL_KEY替换null
? NULL_KEY
// 非空键使用WeakReference进行包装
: new CacheKey<>(key, refQueue);
}
private final int hash;
private CacheKey(K key, ReferenceQueue<K> refQueue) {
super(key, refQueue);
this.hash = System.identityHashCode(key); // compare by identity
}
@Override
public int hashCode() {
return hash;
}
@Override
public boolean equals(Object obj) {
K key;
return obj == this ||
obj != null &&
obj.getClass() == this.getClass() &&
// cleared CacheKey is only equal to itself
(key = this.get()) != null &&
// compare key by identity
key == ((CacheKey<K>) obj).get();
}
void expungeFrom(ConcurrentMap<?, ? extends ConcurrentMap<?, ?>> map,
ConcurrentMap<?, Boolean> reverseMap) {
// removing just by key is always safe here because after a CacheKey
// is cleared and enqueue-ed it is only equal to itself
// (see equals method)...
ConcurrentMap<?, ?> valuesMap = map.remove(this);
// remove also from reverseMap if needed
if (valuesMap != null) {
for (Object cacheValue : valuesMap.values()) {
reverseMap.remove(cacheValue);
}
}
}
}
Factory
工厂类,通过一级缓存的 key 和 parameter 生成最终结果值,同时会把值赋值给二级缓存 WeakReference 弱引用:
private final class Factory implements Supplier<V> {
//一级缓存key
private final K key;
//根据一级缓存key和该参数获取二级缓存value
private final P parameter;
//二级缓存key
private final Object subKey;
//二级缓存容器
private final ConcurrentMap<Object, Supplier<V>> valuesMap;
Factory(K key, P parameter, Object subKey,
ConcurrentMap<Object, Supplier<V>> valuesMap) {
this.key = key;
this.parameter = parameter;
this.subKey = subKey;
this.valuesMap = valuesMap;
}
@Override
public synchronized V get() { // serialize access
// 检查二级缓存的工厂对象是否存在
Supplier<V> supplier = valuesMap.get(subKey);
if (supplier != this) {
// 在等待的时候会发生变化
// might be that we were replaced by a CacheValue
// or were removed because of failure ->
// return null to signal WeakCache.get() to retry
// the loop
return null;
}
// else still us (supplier == this)
V value = null;
try {
//根据一级缓存key和paramter参数生成二级缓存的value值
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
} finally {
if (value == null) { // remove us on failure
valuesMap.remove(subKey, this);
}
}
// the only path to reach here is with non-null value
assert value != null;
// 缓存Factory计算后的值,缓存的值是一个WeakReference类型
CacheValue<V> cacheValue = new CacheValue<>(value);
// 存储起来,用于后期的查找containsValue操作
reverseMap.put(cacheValue, Boolean.TRUE);
/**
* 用新的弱引用替换旧的值
* 注意:初始化时二级缓存value是工厂实例,使用该工厂获取值后,
* 就会替换为获取的值的弱引用,上面的if判断也跟这里相关
*/
if (!valuesMap.replace(subKey, this, cacheValue)) {
throw new AssertionError("Should not reach here");
}
// successfully replaced us with new CacheValue -> return the value
// wrapped by it
return value;
}
}
上面提到的二级缓存都是强用用,但是通过对 ValueFactory 通常应用一级 key 和 paramter 参数生成 value 值后,通过一个弱引用对象引用该 value 值,而在之后的步骤中,会对二级缓存执行 replace,替换二级 key 对应槽的 Supplier 工厂实例。那二级缓存的 value 值也可以是弱引用对象实例。也就是说二级缓存的 value 是强引用和弱引用来回转换。
分析完 WeakCache 私有内部类,接下来分析根据 key 和 paramter 参数值获取二级缓存的弱引用包装的实例对象:
public V get(K key, P parameter) {
Objects.requireNonNull(parameter);
expungeStaleEntries();
// 创建一级缓存的弱引用key
Object cacheKey = CacheKey.valueOf(key, refQueue);
/**
* 根据指定的一级key获取二级缓存容器
* 当弱引用被 clear 后,entries 会被以惰性(lazily)方式被删除
*/
ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
if (valuesMap == null) {// 初始化指定key的二级容器
ConcurrentMap<Object, Supplier<V>> oldValuesMap
= map.putIfAbsent(cacheKey,
valuesMap = new ConcurrentHashMap<>());
if (oldValuesMap != null) {//有可能出现并发
valuesMap = oldValuesMap;
}
}
// 使用指定的一级key和paramter参数创建二级key
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
//获取二级缓存value值,即工厂实例
Supplier<V> supplier = valuesMap.get(subKey);
Factory factory = null;
while (true) {
if (supplier != null) {
// supplier might be a Factory or a CacheValue<V> instance
V value = supplier.get();
/**
* 这里的get()方法有可能返回null
* something changed while we were waiting:
* 1: might be that we were replaced by a CacheValue
* 2: removed because of failure ->
* return null to signal WeakCache.get() to retry the loop
*/
if (value != null) {
return value;
}
}
// else no supplier in cache
// or a supplier that returned null (could be a cleared CacheValue
// or a Factory that wasn't successful in installing the CacheValue)
// lazily construct a Factory
if (factory == null) { // 构造工厂实例
factory = new Factory(key, parameter, subKey, valuesMap);
}
if (supplier == null) {
supplier = valuesMap.putIfAbsent(subKey, factory);
if (supplier == null) {
// successfully installed Factory
supplier = factory;
}
// else retry with winning supplier
} else {
if (valuesMap.replace(subKey, supplier, factory)) {
// successfully replaced
// cleared CacheEntry / unsuccessful Factory
// with our Factory
supplier = factory;
} else {
// retry with current supplier
supplier = valuesMap.get(subKey);
}
}
}
}
获取值的伪代码:
通过二级缓存subkey获取对应的value值
定义一个工厂实例factory
while(true) {
if(value不为null) {
1.通过二级缓存value获取值var,注意该value可能是工厂实例,也可能是弱引用实例,
如果是工厂实例,调用工厂的get方法,会把二级缓存的value值替换成返回对象的弱引用实例
2.不为空则返回
}
if(factory为null) {
factory = new Factory对象;
}
二级缓存value值为factory实例;
}
在文章的开始部分提到一级缓存的 key 和 value 都是弱引用,从 get() 方法可以看出,一级缓存的 key 是 CacheKey 类型,当 key 被回收时,其对应的 value 值也就没有被引用,失去了存在的意义,也就是可以理解为 value 也是弱引用类型。
二级缓存的 key 通过工厂方法(subKeyFactory)生成,其也有可能是弱引用类型实例,这就导致二级缓存可以是弱引用或强引用类型。value 是 CacheValue 类型的对象,从上面的分析可知,CacheValue 继承 WeakReference 类,即 value 是弱引用类型。
至于为什么会有二级缓存。参考了一些资料,主要是因为有可能多个类具有共同的 classloader,为保证唯一性,就要有二级缓存,采用接口数组来唯一标识一个代理类。当 classloader 移除后,会将它所对应的所有缓存都清掉,所以就要采用弱引用类型,而接口数组对应的类肯定一直存在,所以才用了强引用。