I am learning Java Concurrency in Practice
, but some code confused me:
private final ConcurrentHashMap<A, Future<V>> cache = new ConcurrentHashMap<A, Future<V>>();
private final Computable<A, V> c;
public Memoizer(Computable<A, V> c) {
this.c = c;
}
/* (non-Javadoc)
* @see com.demo.buildingblocks.Computable#compute(java.lang.Object)
*/
@Override
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>() {
@Override
public V call() throws Exception {
return c.compute(arg);
}
};
FutureTask<V> ft = new FutureTask<V>(eval);
// what will happen when two threads arrive here at the same time?
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) {
launderThrowable(e);
}
}
}
I just can't understand, since putIfAbsent
can only guarantee put
operation is atomatic,and they all return null, if the two threads can both enter the run
method?
putIfAbsent
guarantees thread safety not only in the sense that it won't corrupt your data, but also in the sense that it always works on an up-to-date copy of the data.
Also, not it returns the previous value from the map, if such a value existed. So the first call to putIfAbsent
would succeed, and return null
, since there is no previous value. The second call will block until the first one succeeds, and then return the first value that was put in the map, causing the second run()
to never be called.