许多人阅读“实践中的Java并发”,但为方便起见,将代码粘贴在此处。
5.19 Memoizer的最终实施:
public class Memoizer<A, V> implements Computable<A, V> {
private final ConcurrentMap<A, Future<V>> cache = new ConcurrentHashMap<A, Future<V>>();
private final Computable<A, V> c;
public Memoizer(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);
f = cache.putIfAbsent(arg, ft);
if (f == null) {
f = ft;
ft.run();
}
}
try {
return f.get();
} catch (CancellationException e) {
cache.remove(arg, f);
} catch (ExecutionException e) {
throw LaunderThrowable.launderThrowable(e.getCause());
}
}
}
}
我的问题是在catch(ExecutionException e)中,应添加以下代码段:
cache.remove(arg, f);
正确?
答案 0 :(得分:1)
如果compute
是幂等的,那么从缓存中删除它会导致不必要的计算,因为下一次调用compute
的结果也将是ExecutionException
。
如果您将f
留在缓存中,则下一次调用将再次运行f.get()
,这将立即返回ExecutionException
,而无需实际执行计算。
所以它看起来更有效率。
请注意,人们会期望compute
是幂等的:如果结果可能会在将来发生变化,那么缓存结果是没有意义的。
答案 1 :(得分:1)
Per JCIP(p.106):
...
Memoizer
如果检测到计算被取消,则从缓存中删除Future
;如果计算可能在将来的尝试中成功,则可能还需要在检测到Future
时删除RuntimeException
。
(强调补充。)
如果没有,那就没有意义了,正如assylias所述。