Search code examples
javamultithreadingconcurrencythread-safety

In Java Concurrency In Practice by Brian Goetz, why not simply use computeIfAbsent


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);
    }

}

Solution

  • 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.)