1:使用同步工具类做一个高效且可伸缩的结果缓存
做缓存一般使用Map结构,当想要取一个键的值时,判断是否存在。如果存在,直接取出即可,如果不存在,则为键计算一个值,将键值对添加到map中。
现在假设我们要将一个字符串转换成整数,整数加1作为返回值,字符串作为键,返回的整数值作为值。构建一个缓存d。
1.1首先创建一个转换接口
package chapter05.class6; /** * 定义一个参数转换接口 * @param <K> 被转换参数 * @param <V> 转换参数 */ public interface Computable<V,K> { V compute(K arg) throws InterruptedException; }
1.2:创建一个缓存对象
package chapter05.class6; import java.util.concurrent.*; /** * 缓存 */ public class Memoizer<V,K> implements Computable<V,K> { //Future<V>相当于一个能够调用将来结果的容器,结果计算过程交由其它完成。携带结果的任务有Future. private final ConcurrentMap<K, Future<V>> cache = new ConcurrentHashMap<>();//使用ConcurrentMap作为缓存。 private final Computable<V,K> c; //转换接口 public Memoizer(Computable<V, K> c) { this.c = c; } @Override public V compute(K arg) throws InterruptedException { while (true) { Future<V> future = cache.get(arg); //得到一个Future<V>,Future<V>如果不为空,则包含要查找的值 if (future == null) { //如果future为空。也就是缓存中没有。 Callable<V> eval=()->{ //创建一个Callable,调用接口中的计算方法compute, return c.compute(arg); //有可能涉及长时间计算
}; FutureTask<V> futureTask = new FutureTask<V>(eval); //将callable传递给futureTask; future = cache.putIfAbsent(arg, futureTask); //判断arg是否在cache中存在,如果不存在,把计算任务委托给futureTask。这时futureTask还不执行,需要调用下面的run()之后才执行。 // 不过如果有新的线程进入调用取arg的值时,就不能添加了,因为arg已经存在了,只是值还不存在,要等到下面计算完成后,取出结果就行了。 if (future == null) { //如果future为空。 future=futureTask; // 将futureTask赋给future futureTask.run(); //调用eval 中的call方法,相当于调用compute } } try { return future.get(); //最后通过future.get()得到想要的值V } catch (Exception e) { } } } }
3:编写测试类
package chapter05.class6; public class MemoizerTest { public static void main(String[] args){ Memoizer memoizer = new Memoizer<Integer,String>((s)->{ //compute方法,被c.compute(arg);调用 return Integer.parseInt(s)+1; }); try { Integer compute = (Integer) memoizer.compute("23"); System.out.println(compute); //24 } catch (InterruptedException e) { e.printStackTrace(); } } }