缓存实现思路

缓存主要为了解决各个组件之间读取速度不匹配问题,比如寄存器是L1的缓存,L1是L2的缓存,L2是L3的缓存,L3是内存的缓存等。通过读Java Concurrency Practice P85,实现了一个简单可以添加和获取数据的缓存。其它的诸如缓存过期,更新缓存等没有实现- -!!

代码

计算接口,用到了装饰者模式。

1
2
3
4

public interface Computable<A,V> {
V compute(A arg);
}

一种计算的实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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保证线程安全来构建缓存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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()方法。如果没有结果,会阻塞当前线程。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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;
}

}