Java Concurrency In Practice by Brian Goetz provides an implementation of Memoizer on pg 108: In Java Concurrency In Practice by Brian Goetz, why is the Memoizer class not annotated with @ThreadSafe?
Instead of this (perhaps over elaborate implementation) what potential downside does the below implementation have?
public class ConcurrentMemoizer<A, V> implements Computable<A, V> {
private final ConcurrentMap<A, V> cache = new ConcurrentHashMap<>();
private final Computable<A, V> c;
public ConcurrentMemoizer(Computable<A, V> c) {
this.c = c;
}
@Override
public V compute(A arg) {
return cache.computeIfAbsent(arg, a -> c.compute(a);
}
}
CodePudding user response:
In Java Concurrency In Practice by Brian Goetz, why not simply use
computeIfAbsent
.
If you are asking why the book didn't use it, one answer is that ConcurrentHashMap
didn't have computeIfAbsent
when the book was published.
If you are asking why you shouldn't use it, the reason is in the javadoc caveat that @Mark Rotteveel pointed out:
"Some attempted update operations on this map by other threads may be blocked while computation is in progress, so the computation should be short and simple."
Explanation: If you look at the source code of computeIfAbsent(key, function)
in a typical ConcurrentHashMap
implementation, you will see that it holds a lock on the submap in which the key
lives. Other threads that access other keys in the same submap will be blocked. This is problematic if the call to function
takes a long time. The book talks about this problem too.
Goetz's final version on page 108 deals with this using a FutureTask
and putIfAbsent
. The blocking time is minimal except when the another thread trying to get the value that you are currently computing.
"... why is the
Memoizer
class not annotated with@ThreadSafe
?"
I'd say that is just an oversight. There are other examples in the book where thread-safe classes haven't been annotated with @ThreadSafe
. (Note that these annotations are only advisory. They don't make the code thread-safe.)