上篇博客针对缓存用个FutureTask来进行处理来解决两个线程可能计算出来同样的值的问题。
在上篇博客中的实现几乎是完美的,它能够表现出非常好的并发性,如果结果计算出来则立即返回,如果其他线程在计算该结果,那么新的线程将一直等待这个结果被计算出来。这样其实也没有彻彻底底的解决两个线程计算出相同的值,但是这种情况出现的概率降的非常低了。
其实这里主要还会出现这种情况原因是compute方法中没有进行检查再执行的操作,所以还会出现两个线程同样值的情况。
这时候需要在Memoizer中加入原子操作来解决,具体代码:
public interface Computable<A,V>{
V compute(A arg) throws InterruptedException;
}
public class ExpensiveFunction implements Computalbe<String ,BigInteger>{
public BigInteger compute(String arg){
return new BigInteger(arg);
}
}
public class Memoizer4<A,V> implements Computable<A,V>{
private final Map<A,Future<V>> cache=new ConcurrentHashMap<A,Future<V>>();
private final Computable<A,V> c;
public Memoizer4(Computable<A,V>,c){
this.c=c;
}
public V compute(A arg) throws InterruptedException{
Future<V> f=cache.get(arg);
if(f==null){
Callable<V> eval=new Callable<V>(){
public V call() throws InterruptedException{
return c.compute(arg);
}
};
FutureTask<V> ft=new FutureTask<V>(eval);
cache.putIfAbsent(arg,ft);
if(f==null){
f=ft;
ft.run();
}
s
}
try{
return f.get();
}catch(ExecutionException e){
}
}
}
其实问题是出现在compute方法中的if没有进行原子操作,所以对ft进行一个二次判断来保持原子操作。
到此位置整个缓存的Demo做完了,通过这样一个例子,能够充分了解同步容器和并发容器的区别和使用。