ITKeyword,专注技术干货聚合推荐

注册 | 登录

为计算机结果建立高效,可伸缩的的高速缓存(java并发编程实践读书笔记四)

wbean 分享于 2011-11-12

2020阿里云最低价产品入口,含代金券(新老用户有优惠),
地址https://www.aliyun.com/minisite/goods

复用已有的计算结果,缩短等待时间,提高吞吐量,代价是占用更多的内存 经典思路:使用一个map,每次计算先在map中查找,找不到的情况下进行计算,把计算结果保存到map中,以便下次计算相同值时直接从map中取得计算结果 /*** 这里定义一个Computable接口,实际的计算类和使用的缓存的计算类都实现该接口* 使用的时候可以方便的切换是否使用缓存** @param <A>* @param <V>*/public interface Computable<A, V> {

V computer(A arg) throws Exception;}

import java.math.BigInteger;import java.util.concurrent.Callable;import java.util.concurrent.CancellationException;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.ExecutionException;import java.util.concurrent.Future;import java.util.concurrent.FutureTask;/*** 实现了Computable接口的计算类,这里模拟了一个复杂耗时的计算**/class ExpensiveFunction implements Computable<String, BigInteger>{

@Override

public BigInteger computer(String arg) throws Exception {

//after deep thought

//...

return new BigInteger(arg);

}}/***使用了缓存的计算类** @param <A>* @param <V>*/public class Memoizer<A,V> implements Computable<A,V> {

//使用并发容器ConcurrentHashMap,增强并发性

//key是计算因子,计算结果使用进行异步调用的Future储存,Future代表了一个计算过程

//计算的时候,先在cache中找该计算过程,获得计算结果;如果cache中不存在该计算过程,则新建一个计算过程, 进行计算。

//在新计算过程 开始计算--得到结果 之前,就把这个计算过程给缓存了,这时如果有多个计算请求arg(计算因子)都是相同的,

//则返回给他们这个计算过程,让其阻塞等待结果,最终实现了每个arg(计算因子)只计算一次。

private final ConcurrentHashMap<A,Future<V>> cache = new ConcurrentHashMap<A, Future<V>>();

private final Computable<A,V> c ;//实际进行计算的类

public Memoizer(Computable<A,V> c){

this.c=c; //把实际计算类的对象传进来

}

@Override

public V computer(final A arg) throws Exception {

while(true){

Future<V> f = cache.get(arg);//直接获取该计算过程

if(f==null){//如果计算过程不存在

//通过Callable接口new一个FutureTask(计算过程,实现了Future接口)

Callable<V> eval = new Callable<V>() {

@Override

public V call() throws Exception {

return c.computer(arg);

}

};

FutureTask<V> ft = new FutureTask<V>(eval);

//cache.putIfAbsent(arg, ft)

//方法:如果arg和ft在ConcurrentHashMap中已建立对应关系,则返回cache.get(arg);

//如果没有建立关系,则简历关系,并且返回null

f=cache.putIfAbsent(arg, ft);

if(f==null){//当缓存中不存在此计算过程

f=ft;//把new出来的FutureTask赋值给f,下面调用f.get()的时候得到的是当前new出来的FutureTask的计算结果

ft.run();//开启计算过程

}

}

try{

//如果计算过程已经存在了,则直接调用FutureTask的阻塞方法get()

//该方法会一直阻塞到计算出结果

return f.get();

}catch(CancellationException e){

cache.remove(arg);//如果计算被取消了,则把缓存中的计算过程移除

}catch(ExecutionException e){

//调用方法处理其他的异常,这里的处理方法在之前的文章Synchronizer...中有提到

throw launderThrowable(e.getCause());

}

}

}

//通过launderThrowable()方法处理其他情况的异常1.如果cause是一个Error,抛出;2.如果是一个RuntimeException,返回该异常;3.其他情况下抛出IllegalStateException

//暂时我也不清楚为什么要这么处理,把Exception return出来有什么用么?还是要回头看java基础啊~~

private Exception launderThrowable(Throwable cause) {

if(cause instanceof Error)

throw new Error(cause);

else if(cause instanceof RuntimeException)

return (Exception) cause;

else throw new IllegalStateException(cause);

}}

复用已有的计算结果,缩短等待时间,提高吞吐量,代价是占用更多的内存 经典思路:使用一个map,每次计算先在map中查找,找不到的情况下进行计算,把计算结果保存到map中,以便下次计算相同值时

相关阅读排行


相关内容推荐

最新文章

×

×

请激活账号

为了能正常使用评论、编辑功能及以后陆续为用户提供的其他产品,请激活账号。

您的注册邮箱: 修改

重新发送激活邮件 进入我的邮箱

如果您没有收到激活邮件,请注意检查垃圾箱。