作者注:走了弯路,guava缓存机制本就支持弱引用模式,参见新博客《guava:LoadingCache缓存机制支持弱引用(WeakReference)》
以前写过一篇博客《java:基于guava缓存(LoadingCache)实现结果缓存避免重复计算
》提到我基于guava的缓存(com.google.common.cache.LoadingCache)实现结果计算结果缓存的类FunctionCached。它很好的解决了常量结果重复计算问题。
但最近我的应用场景有点变化。FunctionCached保存的结果有可能被多次重复使用,也有可能不再被继续使用。我希望这些不再被继续的使用数据能自动被回收,不要占用系统资源,如果万一它真的又被需要,就再次计算,并将结果缓存。
guava的缓存(com.google.common.cache.LoadingCache)的确可以清除闲置超时的对象,但它有个缺陷,只有在下次调用的时候,才会执行清除动作,如果无法预测是否有下次调用,那么有些闲置数据就永远没有机会清除。为解决这个问题我想到了弱引用(WeakReference)应该可以解决这个问题。
于是基于弱引用(WeakReference)在FunctionCached基础上我实现了FunctionCachedWeak类,使用弱引用定义数据,当需要读取数据而引用对象被回收时,重新计算并加入缓存。这样就能解决缓存中闲置数据占用资源的问题。
实现代码
实现代码如下:
FunctionCachedWeak.java,其将FunctionCached(https://gitee.com/l0km/common-java/blob/master/common-base2/src/main/java/net/gdface/utils/FunctionCachedWeak.java)作为成员对象.
import java.lang.ref.WeakReference;
import com.google.common.base.Function;
import com.google.common.cache.CacheBuilder;
/**
* 基于{@link FunctionCached}实现弱引用的类型转转换接口{@link Function},
* @author guyadong
*
* @param <K> 输入参数类型
* @param <V> 输出参数类型
* @see 2.9.3
*/
public class FunctionCachedWeak<K,V> implements Function<K, V>{
private final Function<K, V> getterFunction;
private final FunctionCached<K, WeakReference<V>> functionCached;
private final V defaultValue;
public FunctionCachedWeak(Function<K, V> getterFunction) {
this(getterFunction, null);
}
public FunctionCachedWeak(Function<K, V> getterFunction,V defaultValue) {
this.getterFunction = getterFunction;
this.defaultValue = defaultValue;
functionCached = FunctionCached.of (new AsWeakRef<>(getterFunction));
}
public V getUncached(K key) {
return null == key ? defaultValue : getterFunction.apply(key);
}
public V get(K key) {
WeakReference<V> weakRef = functionCached.get(key);
if(null != weakRef) {
if(weakRef.get() == null) {
/** 弱引用失效时删除对应的key,重新计算 */
System.out.println("reload");// 测试代码用于,弱引用失效时重新加载时显示
functionCached.cache.asMap().remove(key);
return get(key);
}
return weakRef.get();
}
return defaultValue;
}
/**
* 缓存调用
* @see #get(Object)
* @see com.google.common.base.Function#apply(java.lang.Object)
*/
@Override
public V apply(K input) {
return get(input);
}
/**
* 返回{@code getterFunction}的{@link FunctionCached}实例,
* 如果{@code getterFunction}为{@link FunctionCached}实例,则直接返回,否则创建新实例
* @param getterFunction
* @param defaultValue
* @see #of(Function, Object, CacheBuilder)
*/
public static <K, V> FunctionCachedWeak<K, V> of(Function<K, V> getterFunction,V defaultValue){
if(getterFunction instanceof FunctionCachedWeak){
return (FunctionCachedWeak<K, V>) getterFunction;
}
return new FunctionCachedWeak<K, V>(getterFunction, defaultValue);
}
/**
* 返回{@code getterFunction}的{@link FunctionCached}实例(默认实例为{@code null})
* @param getterFunction
* @see #of(Function, Object)
*/
public static <K, V> FunctionCachedWeak<K, V> of(Function<K, V> getterFunction){
return of(getterFunction,null);
}
private static class AsWeakRef<K,V> implements Function<K, WeakReference<V>>{
private final Function<K, V> getterFunction;
public AsWeakRef(Function<K, V> getterFunction) {
this.getterFunction = getterFunction;
}
@Override
public WeakReference<V> apply(K input) {
V v = getterFunction.apply(input);
return null == v ? null : new WeakReference<>(v);
}
}
}
调用示例
调用示例:
FunctionCachedWeakTest.java
import org.junit.Test;
import com.google.common.base.Function;
public class FunctionCachedWeakTest {
@Test
public void testReload() throws InterruptedException {
FunctionCachedWeak<Integer,String> functionCached = FunctionCachedWeak.of(new Function<Integer,String>() {
@Override
public String apply(Integer input) {
return input.toString();
}
});
for(int i=0;i<1000;++i) {
functionCached.get(i);
}
System.gc(); // 显式调用垃圾收集器
int count = 0;
while(true) {
System.out.printf("%s\n", functionCached.get(count++));
Thread.sleep(1000);
}
}
}