Den*_*ang 2 java concurrency thread-safety concurrenthashmap java.util.concurrent
Brian Goetz的Java Concurrency实践提供了一个用于并发使用的高效可伸缩缓存的示例.显示类Memoizer(第108页)实现的示例的最终版本显示了这样的缓存.我想知道为什么有一个内部和外部检查if(f == null).第二个没有任何意义,因为:
这是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(e.getCause());
}
}
}
Run Code Online (Sandbox Code Playgroud)
- 前面有一个检查,前面的最后一步肯定会返回一个非空值
cache.putIfAbsent(arg, ft);
如果只有一个线程调用compute,那么cache.putIfAbsent(arg, ft);将始终返回null,因为没有先前的值.
如果有两个或多个线程同时调用该compute方法,那么只有其中一个线程将null离开cache.putIfAbsent(arg, ft);,其他ft线程将获得null创建的线程的值.
在这种情况下,其他线程丢弃他们的FutureTask实例并继续他们收到的实例 cache.putIfAbsent(arg, ft);
- 第二次检查中的ft.run()没有任何意义,因为此后会立即调用f.get().
您需要runa FutureTask以便get稍后从中获取值.如果你不打电话,run你就不会得到任何价值.创建了FutureTask是得到了存储在缓存中,将线程run,然后get立即返回,因为它已经在该点完成.
但是compute同时调用并且从中得到非空值的其他线程putIfAbsent将转到get调用并等待第一个线程完成该run方法.