您的位置:首页 > 其它

多线程缓存事例

2017-01-09 18:56 183 查看

注:文章示例由上而下,安全级别越高。

示例1.

public interface Computable<A,V>{
V compute(A arg) throws InterruptedException;
}
public class ExpensiveFunction implements Computable<String,BigInteger>{
public BigInteger compute(String arg){
//在经过长时间的计算后
return new BigInteger(arg);
}
}
public class Memoizer1<A,V> implements Computable<A, V>{
@GuardedBy("this")
private final Map<A,V> cache = new HashMap<A, V>();
private final Computable<A, V> c;

public Memoizer1(Computable<A, V> c){
this.c = c;
}
public synchronized V compute(A arg) throws InterruptedException{
V result = cache.get(arg);
if(result ==null){
result = c.compute(arg);
cache.put(arg, result);
}
return result;
}
}

 问题是:HashMap 不是线程安全的,因此采用的是将compute方法进行同步。但是这样只能保证每次只有一个线程执行compute方法,有明显的可伸缩性问题。

 

示例2.

public class Memoizer2<A,V> implements Computable<A, V>{
private final Map<A,V> cache = new ConcurrentHashMap<A, V>();//线程安全,高效
private final Computable<A,V> c;
private Memoizer2(Computable<A,V> c){
this.c = c;
}

public V compute(A arg) throws InterruptedException{
V result = cache.get(arg);
if(result == null ){
result = c.compute(arg);
cache.put(arg,result);
}
return result;
}
}

 示例2问题在于:如果某个线程启动了一个开销很大的计算,而其他线程并不知道这个计算正在进行,那么很可能会重复这个计算。

 

示例3.

public class Memoizer3<A,V> implements Computable<A, V>{
private final Map<A,Future<V>> cache = new ConcurrentHashMap<A, Future<V>>();
private final Computable<A,V> c;
private Memoizer3(Computable<A,V> c){
this.c = c;
}

public V compute(final 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);//不存在,创建FutureTask
f = ft;
cache.put(arg, ft);//注册到map中
ft.run();//开始执行计算
}
try {
return f.get(); //获得最后计算结果
} catch (ExecutionException e) {

}
}
}

FutureTask :表示一个计算的过程,这个过程可能已经计算完成,也可能正在进行。如果有结果可用,那么FutureTask.get将立即返回结果,否则会一直阻塞,直到结果计算出来再将其返回。

示例3问题在于:仍然存在两个线程重复计算的问题。因为if语句块是复合操作(“若没有则添加”),无法保证原子性。解决这个问题也很简单,只要使用ConcurrentMap 中的原子方法 putIfAbsent就可以啦。

请看示例4

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;
private Memoizer4(Computable<A,V> c){
this.c = c;
}

public V compute(final A arg) throws InterruptedException{
while(true){
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);//不存在,创建FutureTask
f = ft;
cache.putIfAbsent(arg, ft);//注册到map中, putIfAbsent原子方法
ft.run();//开始执行计算
}
try {
return f.get(); //获得最后计算结果
} catch (ExecutionException e) {

}
}
}
}

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: