Search code examples
javaconcurrencyjava.util.concurrent

How to do a lazy create and set with AtomicReference in a safe and efficient manner?


I'm looking to lazily create something and cache the results as an optimization. Is the code below safe and efficient, or is there a better way to do this? Is the compare and set loop needed here?

...
AtomicReference<V> fCachedValue = new AtomicReference<>();

public V getLazy() {
    V result = fCachedValue.get();
    if (result == null) {
        result = costlyIdempotentOperation();
        fCachedValue.set(result);
    }
    return result; 
} 

edit: The value being set in my example here from costlyIdempotentOperation() would always be the same no matter what thread called it.


Solution

  • That is not a great system. The problem is that two threads may find that the result == null, and both will set the fCachedValue to their new result value.

    You want to use the compareAndSet(...) method:

    AtomicReference<V> fCachedValue = new AtomicReference<>();
    
    public V getLazy() {
        V result = fCachedValue.get();
        if (result == null) {
            result = costlyIdempotentOperation();
            if (!fCachedValue.compareAndSet(null, result)) {
                return fCachedValue.get();
            }
        }
        return result; 
    } 
    

    If multiple threads get in to the method before it has been initialized, they may all try to create the large result instance. They will all create their own version of it, but the first one to complete the process will be the one who gets to store their result in the AtomicReference. The other threads will complete their work, then dispose of their result and instead use the result instance created by the 'winner'.