缓存实现思路
Posted jinshuai86
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了缓存实现思路相关的知识,希望对你有一定的参考价值。
缓存主要为了解决各个组件之间读取速度不匹配问题,比如寄存器是L1的缓存,L1是L2的缓存,L2是L3的缓存,L3是内存的缓存等。通过读Java Concurrency Practice P85,实现了一个简单可以添加和获取数据的缓存。其它的诸如缓存过期,更新缓存等没有实现- -!!
代码
计算接口,用到了装饰者模式。
public interface Computable<A,V> {
V compute(A arg);
}
一种计算的实现
public class ExpensiveFunction implements Computable<String,BigInteger> {
@Override
public BigInteger compute(String arg) {
// 经过长时间计算后
try {
Thread.sleep(500000);
} catch (InterruptedException ie) {
}
return new BigInteger(arg);
}
}
版本1
通过HashMap时间复杂度为O(1)的特性以及synchronized保证线程安全来构建缓存
public class MyCacheV1<A,V> implements Computable<A,V>{
private Map<A,V> cache = new HashMap<>();
private Computable computable;
public MyCacheV1(Computable computable) {
this.computable = computable;
}
@Override
public synchronized V compute(A arg) {
V result = cache.get(arg);
if (result == null) {
result = (V)computable.compute(arg);
cache.put(arg,result);
}
return result;
}
}
版本2
版本1添加了synchronized,多线程情况下造成性能下降 -> 换成ConcurrentHashMap
public class MyCacheV2<A,V> implements Computable<A,V>{
private Map<A,V> cache = new ConcurrentHashMap<>();
private Computable computable;
public MyCacheV2(Computable computable) {
this.computable = computable;
}
@Override
public V compute(A arg) {
V result = cache.get(arg);
if (result == null) {
result = (V)computable.compute(arg);
cache.put(arg,result);
}
return result;
}
}
版本3
版本2先判断 -> 在计算属于复合操作而且没有加锁导致线程不安全会产生重读计算。如果遇到计算时间非常长的计算,一旦重复会消耗大量的资源。
解决思路:如果其他线程正在计算目标值,当前线程阻塞直到其它线程计算出结果返回即可。
实现:通过FutureTask的get()方法。如果没有结果,会阻塞当前线程。
public class MyCacheV3<A,V> implements Computable<A,V>{
private Map<A,FutureTask> cache = new ConcurrentHashMap<>();
private Computable computable;
public MyCacheV3(Computable computable) {
this.computable = computable;
}
@Override
public V compute(A arg) {
Future future = cache.get(arg);
if (future == null) {
FutureTask futureTask = new FutureTask(new Callable() {
@Override
public V call() throws Exception {
return (V)computable.compute(arg);
}
});
// 用到了ConcurrentHashMap的原子操作
future = cache.putIfAbsent(arg,futureTask);
// 二次判断
if (future == null) {future = futureTask; futureTask.run();}
}
V result = null;
try {
result = (V) future.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
return result;
}
}
以上是关于缓存实现思路的主要内容,如果未能解决你的问题,请参考以下文章
Android获取各个应用程序的缓存文件代码小片段(使用AIDL)