构建高效的结果缓存

本程序使用ConcurrentHashMap,FutureTask构建高效的缓存。

Computable代表高开销的计算任务,接受A类型的入参,返回V类型的结果

 interface Computable<A,V>{//计算组件
	  public V compute(A arg);//高开销的计算
  }

采用装饰者模式对Computable组件进行装饰,增强computable方法,使之能对结果进行缓存。

public class Memoizer<A,V> implements Computable<A,V>{

	//map不直接存放计算的结果,而是存放计算任务Future
	private final ConcurrentMap<A,Future<V>> cache = new ConcurrentHashMap<>();
	
	private final Computable<A,V> c ;//计算组件
	
	public Memoizer(Computable<A,V> c){
		this.c= c;
	}
	
	@Override
	public V compute(final A arg){
	      while(true){
		Future<V> f = cache.get(arg);//返回缓存中的计算任务.
		if(f==null){//(1)若果f为Null,说明在此之前没有线程执行过此任务.若f不为null,说明其他线程已经执行过该计算任务,或者正在执行该计算任务,则本线程可调用Feture.get()返回结果
			
			Callable<V> task =new Callable<V>(){

				@Override
				public V call() throws Exception {
					
					return c.compute(arg);
				}
				
			};
			
			FutureTask<V> ft = new FutureTask<V>(task);//创建任务
			f = cache.putIfAbsent(arg,ft);//向缓存中存放该任务
			if(f==null){(2)//若f==null,说明本线程第一个存放该计算任务.否则,说明其他线程已经存放过该任务且正在执行计算任务,则本线程直接调用f.get()获取结果。
				f= ft;
				ft.run();//执行计算任务并得到计算结果
			}
			
		}
		
		try {
			return f.get();
		} catch (InterruptedException | ExecutionException e) {
			cache.remove(arg);
			e.printStackTrace();
		}
		return null;
             }
	}

}

该程序需注意一下几点:

1.底层采用ConcurrentHashMap保证线程安全性。同时要注意程序中两处使用了if(f==null)的判断。第二处判断是为了处理这样一种情况:两个线程处理同一个计算任务(参数arg 相同),并且都通过第一处的if判断,各自new出了相同任务,都执行了 cache.putIfAbsent(arg,ft)。在第一个执行cache.putIfAbsent(arg,ft)操作的线程中,函数的返回值f 为Null,然后执行 ft.run() 即执行计算任务。此时第二个线程中f 的值不为null,不执行ft.run() ,执行f.get() 得到结果。

2.在cache中保存的并不是类型为V的计算结果(计算过程比较耗时)而是任务FutureTask。这减少了线程同时通过第一个if(f==null)的几率。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值