Search code examples
javamultithreadingconcurrencylazy-initialization

Refering "this" in a lazy initialization supplier?


For business decision applications, I run into a lot of cases where I must cache an expensive value with lazy initialization. So I leveraged generics and a Supplier lambda to encapsulate a lazy initialization.

import java.util.function.Supplier;

public final class LazyProperty<T> {
    private final Supplier<T> supplier;
    private volatile T value;

    private LazyProperty(Supplier<T> supplier) { 
        this.supplier = supplier;
    }
    public T get() { 
        if (value == null) { 
            synchronized(this) { 
                if (value == null) { 
                    value = supplier.get();
                }
            }
        }
        return value;
    }

    public static <T> LazyProperty<T> forSupplier(Supplier<T> supplier) { 
        return new LazyProperty<T>(supplier);
    }
}

But I'd like to be able to use this also in cases where I can't initialize a property until after the object is created, because the object can only calculate this property after it is created (usually needing context of itself or other objects). However, this often requires a reference to this in the supplier function.

public class MyClass {
    private final LazyProperty<BigDecimal> expensiveVal = 
         LazyProperty.forSupplier(() -> calculateExpensiveVal(this));

    public BigDecimal getExpensiveVal() { 
        return expensiveVal.get();
    }
}

As long as I can guarantee the LazyProperty's get() function is only called after MyClass is constructed (via the getExpensiveVal() method), there shouldn't be any partial construction issues due to the this reference in the supplier, correct?


Solution

  • Your code will have one Problem which depends on the implementation of method calculateExpensiveVal.

    1. if calculateExpensiveVal calls getExpensiveVal on the passed reference of MyClass you will get NullPointerException.

    2. if calculateExpensiveVal creates a thread and pass the reference of MyClass, again you may run into the same problem as point 1.

    But if you guarantee calculateExpensiveVal is not doing any of the things, then your code stand correct from Thread safety Perspective. MyClass will never be seen partially constructed because of the final gaurantees provided by the JMM

    After saying that even though your *calculateExpensiveVal may employ any one or both those points you are only going to have problem in getExpensiveVal method with NullPointerException.

    your lazyProperty.get method is already thread safe so there woun'd be any problem.

    Because you will always see fully constructed Supplier object because of final keyword (only if you didn't escaped 'this' reference to another thread) and you already have used volatile for value field which takes care of seeing fully constructed value object.