缓存
写入前,先排重,同时要防止重复写(两个线程同时写入),或者是重复读(已有线程写入,正在计算,另一个线程可以不必继续访问,直接等待结果就好)。
关键点:
1- ConcurrentHashMap分段锁。
2- Callable启动线程,返回Future对象。
代码:
public interface Computable<K,V> {
V compute(K arg)throws InterruptedException;
}
public class Memoizer<K,V> implements Computable<K,V> {
private final Map<K,Future<V>> cache = new ConcurrentHashMap<>();
private Computable<K,V> c=null;
public Memoizer(Computable<K,V> c) { this.c = c; }
public V compute(final K arg) throws InterruptedException{
while(true){
Future<V> f = cache.get(arg);
if(f==null){
Callable<V> call = new Callable<V>() {
@Override
public V call() throws InterruptedException {
return c.compute(arg);
}
};
FutureTask<V> ft = new FutureTask<V>(call);
f = cache.putIfAbsent(arg,ft);
if(f==null){
f = ft;
ft.run();
}
}
try{
return f.get();
}catch (CancellationException e){
cache.remove(arg,f);
}catch(ExecutionException e){
e.printStackTrace();
}
}
}
}
分析:
在多线程情况下,建立高效缓存,首先要考虑的是缓存的重复写,和重复读问题。
如何解决重复写
1:利用ConcurrentHashMap解决重复写,相同的key覆盖,同时分段锁segment更加精细,有利于数据的同步。
2:在写入前检查是否已有线程写入cache.get(arg)。
3:没有写入再通过ConcurrentHashMap的cache.putIfAbsent(arg,ft)方法,提供原子性写入(防止两个线程同时写入的情况。)
4:如果写入了,则直接等待正在进行计算,f.get()处于阻塞状态,直到返回结果。
说明
受教与《并发编程实践》