在JCIP书中,清单5.19最终实现了内存程序。我的问题是:
代码:
public class Memorizer<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 Memorizer(Computable<A, V> c) { this.c = c; }
public V compute(final A arg) throws InterruptedException {
while (true) { //<==== WHY?
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(e.getCause());
}
}
}
}
发布于 2012-12-20 23:42:34
1)由于原子putIfAbsent()的存在,这里有无穷无尽的while循环吗?
这里的while循环用于在取消计算时重复计算(try
中的第一个例子)。
2)应该使用putIfAbsent()内部的while循环而不是客户端代码吗?
不,请读一下putIfAbsent
做了什么。它只试着放置一个物体一次。
3) while循环应该在较小的范围内仅包装putIfAbsent()吗?
不,不应该。见第一。
4)循环的可读性不佳。
你可以自由地提供更好的东西。事实上,当你不得不尝试做一些事情直到它成功的时候,这个建筑套房是非常适合的情况。
发布于 2012-12-20 22:45:50
不,不能缩小while循环的范围。您希望对缓存中的值执行f.get()
。如果映射中没有arg
的值,则希望对结果执行get()
,否则要获得映射中的arg
和get()
的现有值。
问题是这个实现中没有锁,所以在检查是否有值和试图插入值之间,另一个线程可能已经插入了自己的值。同样,在插入失败和检索之间,可以将值从缓存中删除(由于一个CancellationException
)。由于这些故障情况,您可以在while(true)
中旋转,直到您可以将规范值从映射中提取出来,或者将一个新值插入到映射中(使您的值规范化)。
您似乎可以尝试将更多的f.get()
从循环中移出,但是由于存在CancellationException
的风险,因此您需要继续尝试。
https://stackoverflow.com/questions/13981595
复制相似问题