我正在寻找Brian Goetz的并发书中以下代码的解释。
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(e.getCause());
}
}
}
另外,在putIfAbsent()调用之后为什么语句f = ft;
而不是直接执行ft.run()?
答案 0 :(得分:1)
putIfAbsent
的返回值是现有的一个(如果有的话)或null
如果没有,我们将新的一个放入。
f = cache.putIfAbsent(arg, ft);
if (f == null) {
f = ft;
ft.run();
}
所以if ( f == null )
表示“我们是否将ft
放入缓存中了?”。显然,如果我们 将其放入缓存中,我们现在需要将f
设置为缓存中的ft
,即ft
。
如果我们没有将f
放入缓存中,那么putIfAbsent
已经是缓存中的那个,因为它是{{1}}返回的值。
答案 1 :(得分:0)
因为您正在返回f.get()并可能从缓存中删除f。这允许一段代码适用于所有实例。
try {
return f.get();
} catch (CancellationException e) {
cache.remove(arg, f);
如果你没有用上面对ft的引用替换f,那么每次putIfAbsent返回null都会得到一个NPE。
答案 2 :(得分:0)
代码的想法如下。请求计算某些值来自不同的线程。如果一个线程已启动某些值的计算,则需要相同结果的其他线程不应重复计算,而应等待初始计算。计算完成后,结果将保留在缓存中。
这种模式的示例是加载java类。如果正在加载一个类,而另一个线程也请求加载同一个类,则它本身不应该加载它,而是等待第一个线程的结果,这样总是不会超过给定类的实例,加载由同一个类加载器..