Java 学习:WeakCache 类

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 移除后,会将它所对应的所有缓存都清掉,所以就要采用弱引用类型,而接口数组对应的类肯定一直存在,所以才用了强引用。

猜你喜欢

转载自blog.csdn.net/dilixinxixitong2009/article/details/88362066
今日推荐